LCOV - code coverage report
Current view: top level - compositor - drawable.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 697 771 90.4 %
Date: 2021-04-29 23:48:07 Functions: 39 39 100.0 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2000-2012
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / Scene Compositor sub-project
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : #include "drawable.h"
      27             : #include "visual_manager.h"
      28             : #include "nodes_stacks.h"
      29             : 
      30             : 
      31             : 
      32             : /*default draw routine*/
      33       47959 : void drawable_draw(Drawable *drawable, GF_TraverseState *tr_state)
      34             : {
      35       47959 :         visual_2d_texture_path(tr_state->visual, tr_state->ctx->drawable->path, tr_state->ctx, tr_state);
      36       47959 :         visual_2d_draw_path(tr_state->visual, tr_state->ctx->drawable->path, tr_state->ctx, NULL, NULL, tr_state);
      37       47959 : }
      38             : 
      39             : /*default point_over routine*/
      40             : #ifndef GPAC_DISABLE_VRML
      41       88567 : void vrml_drawable_pick(Drawable *drawable, GF_TraverseState *tr_state)
      42             : {
      43             :         DrawAspect2D asp;
      44             :         GF_Matrix2D inv_2d;
      45             :         StrikeInfo2D *si;
      46             :         Fixed x, y;
      47             :         u32 i, count;
      48       88567 :         GF_Node *appear = tr_state->override_appearance ? tr_state->override_appearance : tr_state->appear;
      49       88567 :         GF_Compositor *compositor = tr_state->visual->compositor;
      50             : 
      51             : #ifndef GPAC_DISABLE_3D
      52       88567 :         if (tr_state->visual->type_3d) {
      53         234 :                 visual_3d_vrml_drawable_pick(drawable->node, tr_state, NULL, drawable);
      54         234 :                 return;
      55             :         }
      56             : #endif
      57       88333 :         gf_mx2d_copy(inv_2d, tr_state->transform);
      58       88333 :         gf_mx2d_inverse(&inv_2d);
      59       88333 :         x = tr_state->ray.orig.x;
      60       88333 :         y = tr_state->ray.orig.y;
      61       88333 :         gf_mx2d_apply_coords(&inv_2d, &x, &y);
      62             : 
      63             :         memset(&asp, 0, sizeof(DrawAspect2D));
      64       88333 :         drawable_get_aspect_2d_mpeg4(drawable->node, &asp, tr_state);
      65             : 
      66             :         /*MPEG-4 picking is always on regardless of color properties*/
      67             :         if (/*tr_state->ctx->aspect.fill_texture */
      68             :             /* (tr_state->pick_type<PICK_FULL) */
      69             :             /* GF_COL_A(asp.fill_color)*/
      70             :             1) {
      71       88333 :                 if (gf_path_point_over(drawable->path, x, y)) {
      72             :                         goto picked;
      73             :                 }
      74             :         }
      75             : 
      76       86072 :         if (asp.pen_props.width || asp.line_texture ) {
      77       22453 :                 si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, appear, NULL, 0, NULL);
      78       22453 :                 if (si && si->outline && gf_path_point_over(si->outline, x, y)) {
      79             :                         goto picked;
      80             :                 }
      81             :         }
      82             :         return;
      83             : 
      84        2278 : picked:
      85        2278 :         compositor->hit_local_point.x = x;
      86        2278 :         compositor->hit_local_point.y = y;
      87        2278 :         compositor->hit_local_point.z = 0;
      88             : 
      89        2278 :         gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform);
      90        2278 :         gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d);
      91             : 
      92        2278 :         gf_list_reset(compositor->hit_use_stack);
      93        2278 :         compositor->hit_node = drawable->node;
      94        2278 :         compositor->hit_use_dom_events = 0;
      95        2278 :         compositor->hit_normal.x = compositor->hit_normal.y = 0;
      96        2278 :         compositor->hit_normal.z = FIX_ONE;
      97        2278 :         compositor->hit_texcoords.x = gf_divfix(x - drawable->path->bbox.x, drawable->path->bbox.width);
      98        2278 :         compositor->hit_texcoords.y = FIX_ONE - gf_divfix(drawable->path->bbox.y - y, drawable->path->bbox.height);
      99             : 
     100             : #ifndef GPAC_DISABLE_VRML
     101        2278 :         if (compositor_is_composite_texture(appear)) {
     102          68 :                 compositor->hit_appear = appear;
     103             :         } else
     104             : #endif
     105             :         {
     106        2210 :                 compositor->hit_appear = NULL;
     107             :         }
     108        2278 :         compositor->hit_text = NULL;
     109             : 
     110        2278 :         gf_list_reset(tr_state->visual->compositor->sensors);
     111        2278 :         count = gf_list_count(tr_state->vrml_sensors);
     112        4314 :         for (i=0; i<count; i++) {
     113        2036 :                 gf_list_add(tr_state->visual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
     114             :         }
     115             : }
     116             : #endif /*GPAC_DISABLE_VRML*/
     117             : 
     118        1277 : void drawable_init_ex(Drawable *tmp)
     119             : {
     120        1277 :         tmp->path = gf_path_new();
     121        1277 :         GF_SAFEALLOC(tmp->dri, DRInfo);
     122        1277 :         if (tmp->dri) {
     123             :                 /*allocate a default bounds container*/
     124        1277 :                 GF_SAFEALLOC(tmp->dri->current_bounds, BoundInfo);
     125             :         }
     126        1277 : }
     127             : 
     128        4822 : Drawable *drawable_new()
     129             : {
     130             :         Drawable *tmp;
     131        4822 :         GF_SAFEALLOC(tmp, Drawable)
     132        4822 :         if (!tmp) {
     133           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate drawable object\n"));
     134             :                 return NULL;
     135             :         }
     136        4822 :         tmp->path = gf_path_new();
     137             :         /*allocate a default visual container*/
     138        4822 :         GF_SAFEALLOC(tmp->dri, DRInfo);
     139        4822 :         if (tmp->dri) {
     140             :                 /*allocate a default bounds container*/
     141        4822 :                 GF_SAFEALLOC(tmp->dri->current_bounds, BoundInfo);
     142             :         }
     143             :         
     144        4822 :         if (!tmp->dri || !tmp->dri->current_bounds) {
     145           0 :                 if (tmp->dri) gf_free(tmp->dri);
     146           0 :                 gf_path_del(tmp->path);
     147           0 :                 gf_free(tmp);
     148           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate drawable object bounds\n"));
     149             :                 return NULL;
     150             :         }
     151             :         return tmp;
     152             : }
     153             : 
     154        1813 : void drawable_reset_bounds(Drawable *dr, GF_VisualManager *visual)
     155             : {
     156             :         DRInfo *dri;
     157             :         BoundInfo *bi, *_cur;
     158             : 
     159        1813 :         dri = dr->dri;
     160        3633 :         while (dri) {
     161        1814 :                 if (dri->visual != visual) {
     162           7 :                         dri = dri->next;
     163           7 :                         continue;
     164             :                 }
     165             :                 /*destroy previous bounds only, since current ones are always used during traversing*/
     166        1807 :                 bi = dri->previous_bounds;
     167        5549 :                 while (bi) {
     168             :                         _cur = bi;
     169        1935 :                         bi = bi->next;
     170        1935 :                         gf_free(_cur);
     171             :                 }
     172        1807 :                 dri->previous_bounds = NULL;
     173        1807 :                 return;
     174             :         }
     175             : }
     176             : 
     177        6098 : void drawable_del_ex(Drawable *dr, GF_Compositor *compositor, Bool no_free)
     178             : {
     179             :         StrikeInfo2D *si;
     180             :         DRInfo *dri;
     181             :         BoundInfo *bi, *_cur;
     182             : 
     183             : 
     184             :         /*remove node from all visuals it's on*/
     185        6098 :         dri = dr->dri;
     186       18300 :         while (dri) {
     187             :                 DRInfo *cur;
     188        6104 :                 Bool is_reg = compositor ? gf_sc_visual_is_registered(compositor, dri->visual) : 0;
     189             : 
     190        6104 :                 bi = dri->current_bounds;
     191       20612 :                 while (bi) {
     192             :                         _cur = bi;
     193        8404 :                         if (is_reg && bi->clip.width) {
     194        5562 :                                 ra_add(&dri->visual->to_redraw, &bi->clip);
     195             :                         }
     196        8404 :                         bi = bi->next;
     197        8404 :                         gf_free(_cur);
     198             :                 }
     199        6104 :                 bi = dri->previous_bounds;
     200       16013 :                 while (bi) {
     201             :                         _cur = bi;
     202        3805 :                         if (is_reg && bi->clip.width) {
     203           1 :                                 ra_add(&dri->visual->to_redraw, &bi->clip);
     204             :                         }
     205        3805 :                         bi = bi->next;
     206        3805 :                         gf_free(_cur);
     207             :                 }
     208        6104 :                 if (is_reg) visual_2d_drawable_delete(dri->visual, dr);
     209             :                 cur = dri;
     210        6104 :                 dri = dri->next;
     211        6104 :                 gf_free(cur);
     212             :         }
     213        6098 :         if (compositor) {
     214        6098 :                 gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
     215             : 
     216             :                 /*check node isn't being tracked*/
     217        6098 :                 if (compositor->grab_node==dr->node)
     218           0 :                         compositor->grab_node = NULL;
     219             : 
     220        6098 :                 if (compositor->focus_node==dr->node) {
     221           0 :                         compositor->focus_node = NULL;
     222           0 :                         compositor->focus_text_type = 0;
     223             :                 }
     224        6098 :                 if (compositor->hit_node==dr->node) compositor->hit_node = NULL;
     225        6098 :                 if (compositor->hit_text==dr->node) compositor->hit_text = NULL;
     226             :         }
     227             : 
     228             :         /*remove path object*/
     229        6098 :         if (dr->path) gf_path_del(dr->path);
     230             : 
     231             : #ifndef GPAC_DISABLE_3D
     232        6098 :         if (dr->mesh) mesh_free(dr->mesh);
     233             : #endif
     234             : 
     235        6098 :         si = dr->outline;
     236       13229 :         while (si) {
     237        1033 :                 StrikeInfo2D *next = si->next;
     238             :                 /*remove from main strike list*/
     239        1033 :                 if (compositor) gf_list_del_item(compositor->strike_bank, si);
     240        1033 :                 delete_strikeinfo2d(si);
     241             :                 si = next;
     242             :         }
     243        6098 :         if (!no_free)
     244        4821 :                 gf_free(dr);
     245        6098 : }
     246             : 
     247        4217 : void drawable_del(Drawable *dr)
     248             : {
     249        4217 :         GF_Compositor *compositor = gf_sc_get_compositor(dr->node);
     250        4217 :         drawable_del_ex(dr, compositor, GF_FALSE);
     251        4217 : }
     252        3266 : void drawable_node_del(GF_Node *node)
     253             : {
     254        3266 :         drawable_del( (Drawable *)gf_node_get_private(node) );
     255        3266 : }
     256             : 
     257        3888 : Drawable *drawable_stack_new(GF_Compositor *compositor, GF_Node *node)
     258             : {
     259        3888 :         Drawable *stack = drawable_new();
     260        3888 :         stack->node = node;
     261        3888 :         gf_node_set_private(node, stack);
     262        3888 :         return stack;
     263             : }
     264             : 
     265      142320 : static BoundInfo *drawable_check_alloc_bounds(struct _drawable_context *ctx, GF_VisualManager *visual)
     266             : {
     267             :         DRInfo *dri, *prev;
     268             :         BoundInfo *bi, *_prev;
     269             : 
     270             :         /*get bounds info for this visual manager*/
     271             :         prev = NULL;
     272      142320 :         dri = ctx->drawable->dri;
     273      142717 :         while (dri) {
     274      142710 :                 if (dri->visual == visual) break;
     275        4357 :                 if (!dri->visual) {
     276        3960 :                         dri->visual = visual;
     277             :                         break;
     278             :                 }
     279             :                 prev = dri;
     280         397 :                 dri = dri->next;
     281             :         }
     282      142320 :         if (!dri) {
     283           7 :                 GF_SAFEALLOC(dri, DRInfo);
     284           7 :                 if (!dri) return NULL;
     285           7 :                 dri->visual = visual;
     286           7 :                 if (prev) prev->next = dri;
     287           0 :                 else ctx->drawable->dri = dri;
     288           7 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Allocating new bound info storage on visual %08x for drawable %s\n", visual, gf_node_get_class_name(ctx->drawable->node)));
     289             :         }
     290             : 
     291             :         /*get available bound info slot*/
     292             :         _prev = NULL;
     293      142320 :         bi = dri->current_bounds;
     294      348408 :         while (bi) {
     295      340360 :                 if (!bi->clip.width) break;
     296             :                 _prev = bi;
     297      206088 :                 bi = bi->next;
     298             :         }
     299      142320 :         if (!bi) {
     300        8048 :                 GF_SAFEALLOC(bi, BoundInfo);
     301        8048 :                 if (!bi) return NULL;
     302        8048 :                 if (_prev) {
     303             : //                      assert(!_prev->next);
     304        4383 :                         _prev->next = bi;
     305             :                 }
     306             :                 else {
     307             : //                      assert(!dri->current_bounds);
     308        3665 :                         dri->current_bounds = bi;
     309             :                 }
     310             :                 //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Allocating new bound info for drawable %s\n", gf_node_get_class_name(ctx->drawable->node)));
     311             :         }
     312             :         /*reset next bound info*/
     313      142320 :         if (bi->next) bi->next->clip.width = 0;
     314             :         return bi;
     315             : }
     316             : 
     317       64318 : void drawable_reset_group_highlight(GF_TraverseState *tr_state, GF_Node *n)
     318             : {
     319       64318 :         Drawable *hlight = tr_state->visual->compositor->focus_highlight;
     320       64318 :         if (hlight && (n == gf_node_get_private(hlight->node))) gf_node_set_private(hlight->node, NULL);
     321       64318 : }
     322             : 
     323        8888 : void drawable_mark_modified(Drawable *drawable, GF_TraverseState *tr_state)
     324             : {
     325             :         /*mark drawable as modified*/
     326        8888 :         drawable->flags |= tr_state->visual->bounds_tracker_modif_flag;
     327             :         /*and remove overlay flag*/
     328        8888 :         drawable->flags &= ~DRAWABLE_IS_OVERLAY;
     329             : 
     330        8888 :         drawable_reset_group_highlight(tr_state, drawable->node);
     331        8888 : }
     332             : 
     333             : /*move current bounds to previous bounds*/
     334      103218 : Bool drawable_flush_bounds(Drawable *drawable, GF_VisualManager *on_visual, u32 mode2d)
     335             : {
     336             :         Bool was_drawn;
     337             :         DRInfo *dri;
     338             :         BoundInfo *tmp;
     339             : 
     340             :         /*reset node modified flag*/
     341      103218 :         drawable->flags &= ~DRAWABLE_HAS_CHANGED;
     342      103218 :         if (drawable->flags & DRAWABLE_HAS_CHANGED_IN_LAST_TRAVERSE) {
     343          15 :                 drawable->flags |= DRAWABLE_HAS_CHANGED;
     344          15 :                 drawable->flags &= ~DRAWABLE_HAS_CHANGED_IN_LAST_TRAVERSE;
     345             :         }
     346             : 
     347      103218 :         dri = drawable->dri;
     348      206832 :         while (dri) {
     349      103608 :                 if (dri->visual == on_visual) break;
     350         396 :                 dri = dri->next;
     351             :         }
     352      103218 :         if (!dri) return 0;
     353             : 
     354      103212 :         was_drawn = (dri->current_bounds && dri->current_bounds->clip.width) ? 1 : 0;
     355             : 
     356      103212 :         if (mode2d) {
     357             :                 /*permanent direct drawing mode, destroy previous bounds*/
     358         648 :                 if (mode2d==1) {
     359         554 :                         if (dri->previous_bounds) {
     360           0 :                                 GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Destroying previous bounds info for drawable %s\n", gf_node_get_class_name(drawable->node)));
     361           0 :                                 while (dri->previous_bounds) {
     362             :                                         BoundInfo *bi = dri->previous_bounds;
     363           0 :                                         dri->previous_bounds = bi->next;
     364           0 :                                         gf_free(bi);
     365             :                                 }
     366             :                         }
     367             :                 }
     368             :         }
     369             :         /*indirect drawing, flush bounds*/
     370             :         else {
     371      102564 :                 tmp = dri->previous_bounds;
     372      102564 :                 dri->previous_bounds = dri->current_bounds;
     373      102564 :                 dri->current_bounds = tmp;
     374             :         }
     375             :         /*reset first allocated bound*/
     376      103212 :         if (dri->current_bounds) dri->current_bounds->clip.width = 0;
     377             : 
     378      103212 :         drawable->flags &= ~DRAWABLE_DRAWN_ON_VISUAL;
     379      103212 :         return was_drawn;
     380             : }
     381             : 
     382             : /*
     383             :         return 1 if same bound is found in previous list (and remove it from the list)
     384             :         return 0 otherwise
     385             : */
     386             : 
     387      126320 : Bool drawable_has_same_bounds(struct _drawable_context *ctx, GF_VisualManager *visual)
     388             : {
     389             :         DRInfo *dri;
     390             :         BoundInfo *bi;
     391             : 
     392      126320 :         dri = ctx->drawable->dri;
     393      253030 :         while (dri) {
     394      126710 :                 if (dri->visual == visual) break;
     395         390 :                 dri = dri->next;
     396             :         }
     397      126320 :         if (!dri) return 0;
     398             : 
     399      126320 :         bi = dri->previous_bounds;
     400      469090 :         while (bi) {
     401      327849 :                 if (
     402             :                     /*if 0, end of bounds used in the previous pass*/
     403      327849 :                     bi->clip.width
     404             :                     /*we need the same Appearance || parent <use>*/
     405      158103 :                     && (bi->extra_check == ctx->appear)
     406             :                     /*we need exact same cliper*/
     407      155514 :                     && (bi->clip.x==ctx->bi->clip.x) && (bi->clip.y==ctx->bi->clip.y)
     408      114314 :                     && (bi->clip.width==ctx->bi->clip.width) && (bi->clip.height==ctx->bi->clip.height)
     409             :                     /*only check x and y (if w or h have changed, object has changed -> bounds info has been reset*/
     410      113480 :                     && (bi->unclip.x==ctx->bi->unclip.x) && (bi->unclip.y==ctx->bi->unclip.y)
     411             :                 ) {
     412             :                         /*remove*/
     413      111399 :                         bi->clip.width = 0;
     414      111399 :                         return 1;
     415             :                 }
     416      216450 :                 bi = bi->next;
     417             :         }
     418             :         return 0;
     419             : }
     420             : 
     421             : /*
     422             :         return any previous bounds related to the same visual in @rc if any
     423             :         if nothing found return 0
     424             : */
     425      123456 : Bool drawable_get_previous_bound(Drawable *drawable, GF_IRect *rc, GF_VisualManager *visual)
     426             : {
     427             :         DRInfo *dri;
     428             :         BoundInfo *bi;
     429             : 
     430      123456 :         dri = drawable->dri;
     431      247603 :         while (dri) {
     432      124147 :                 if (dri->visual == visual) break;
     433         691 :                 dri = dri->next;
     434             :         }
     435      123456 :         if (!dri) return 0;
     436             : 
     437      123456 :         bi = dri->previous_bounds;
     438      395228 :         while (bi) {
     439      166795 :                 if (bi->clip.width) {
     440       18479 :                         *rc = bi->clip;
     441       18479 :                         bi->clip.width = 0;
     442       18479 :                         return 1;
     443             :                 }
     444      148316 :                 bi = bi->next;
     445             :         }
     446             :         return 0;
     447             : }
     448             : 
     449             : 
     450             : 
     451        5542 : DrawableContext *NewDrawableContext()
     452             : {
     453             :         DrawableContext *tmp;
     454        5542 :         GF_SAFEALLOC(tmp, DrawableContext);
     455        5542 :         return tmp;
     456             : }
     457        5539 : void DeleteDrawableContext(DrawableContext *ctx)
     458             : {
     459        5539 :         drawctx_reset(ctx);
     460        5539 :         gf_free(ctx);
     461        5539 : }
     462      184205 : void drawctx_reset(DrawableContext *ctx)
     463             : {
     464      184205 :         DrawableContext *next = ctx->next;
     465      184205 :         if (ctx->col_mat) gf_free(ctx->col_mat);
     466             :         memset(ctx, 0, sizeof(DrawableContext));
     467      184205 :         ctx->next = next;
     468             : 
     469             :         /*by default all nodes are transparent*/
     470      184205 :         ctx->flags |= CTX_IS_TRANSPARENT;
     471             : 
     472             :         /*BIFS has default value for 2D appearance ...*/
     473      184205 :         ctx->aspect.fill_color = 0xFFCCCCCC;
     474      184205 :         ctx->aspect.line_color = 0xFFCCCCCC;
     475      184205 :         ctx->aspect.pen_props.width = FIX_ONE;
     476      184205 :         ctx->aspect.pen_props.cap = GF_LINE_CAP_FLAT;
     477      184205 :         ctx->aspect.pen_props.join = GF_LINE_JOIN_BEVEL;
     478      184205 :         ctx->aspect.pen_props.miterLimit = 4*FIX_ONE;
     479             : 
     480      184205 : }
     481             : 
     482      130924 : void drawctx_update_info(DrawableContext *ctx, GF_VisualManager *visual)
     483             : {
     484             :         DRInfo *dri;
     485             :         Bool moved, need_redraw, drawn;
     486      130924 :         need_redraw = (ctx->flags & CTX_REDRAW_MASK) ? 1 : 0;
     487             : 
     488             :         drawn = 0;
     489      130924 :         dri = ctx->drawable->dri;
     490      262238 :         while (dri) {
     491      131314 :                 if (dri->visual==visual) {
     492      130924 :                         if (dri->current_bounds && dri->current_bounds->clip.width) drawn = 1;
     493             :                         break;
     494             :                 }
     495         390 :                 dri = dri->next;
     496             :         }
     497             :         if (drawn) {
     498      130924 :                 ctx->drawable->flags |= DRAWABLE_DRAWN_ON_VISUAL;
     499             :                 /*node has been modified, do not check bounds, just assumed it moved*/
     500      130924 :                 if (ctx->drawable->flags & DRAWABLE_HAS_CHANGED) {
     501             :                         moved = 1;
     502             :                 } else {
     503             :                         /*check if same bounds are used*/
     504      126320 :                         moved = !drawable_has_same_bounds(ctx, visual);
     505             :                 }
     506             : 
     507      130924 :                 if (need_redraw || moved)
     508       29655 :                         ctx->flags |= CTX_REDRAW_MASK;
     509             :         }
     510             : 
     511             :         /*in all cases reset dirty flag of appearance and its sub-nodes*/
     512             :         //if (ctx->flags & CTX_HAS_APPEARANCE) gf_node_dirty_reset(ctx->appear);
     513      130924 : }
     514             : 
     515             : #ifndef GPAC_DISABLE_VRML
     516      171682 : static Bool drawable_lineprops_dirty(GF_Node *node)
     517             : {
     518      171682 :         LinePropStack *st = (LinePropStack *)gf_node_get_private(node);
     519      171682 :         if (!st) return 0;
     520             : 
     521      171682 :         if (st->compositor->current_frame == st->last_mod_time) return st->is_dirty;
     522       48115 :         if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) {
     523        2522 :                 gf_node_dirty_clear(node, 0);
     524        2522 :                 st->is_dirty = 1;
     525             :         } else {
     526       45593 :                 st->is_dirty = 0;
     527             :         }
     528       48115 :         st->last_mod_time = st->compositor->current_frame;
     529       48115 :         return st->is_dirty;
     530             : }
     531             : 
     532      249866 : u32 drawable_get_aspect_2d_mpeg4(GF_Node *node, DrawAspect2D *asp, GF_TraverseState *tr_state)
     533             : {
     534             :         M_Material2D *m = NULL;
     535             :         M_LineProperties *LP;
     536             :         M_XLineProperties *XLP;
     537      249866 :         GF_Node *appear = tr_state->override_appearance ? tr_state->override_appearance : tr_state->appear;
     538             :         u32 ret = 0;
     539             : 
     540      249866 :         asp->pen_props.cap = GF_LINE_CAP_FLAT;
     541      249866 :         asp->pen_props.join = GF_LINE_JOIN_MITER;
     542      249866 :         asp->pen_props.align = GF_PATH_LINE_CENTER;
     543      249866 :         asp->pen_props.miterLimit = 4*FIX_ONE;
     544      249866 :         asp->line_color = 0xFFCCCCCC;
     545      249866 :         asp->pen_props.width = 0;
     546             : 
     547      249866 :         if (appear == NULL) goto check_default;
     548             : 
     549      247923 :         if ( ((M_Appearance *) appear)->texture ) {
     550       41686 :                 asp->fill_texture = gf_sc_texture_get_handler( ((M_Appearance *) appear)->texture );
     551             :         }
     552             : 
     553             : 
     554      247923 :         m = (M_Material2D *) ((M_Appearance *)appear)->material;
     555      247923 :         if ( m == NULL) {
     556       14662 :                 asp->fill_color &= 0x00FFFFFF;
     557       14662 :                 goto check_default;
     558             :         }
     559      233261 :         switch (gf_node_get_tag((GF_Node *) m) ) {
     560             :         case TAG_MPEG4_Material2D:
     561             :                 break;
     562          16 :         case TAG_MPEG4_Material:
     563             : #ifndef GPAC_DISABLE_X3D
     564             :         case TAG_X3D_Material:
     565             : #endif
     566             :         {
     567             :                 M_Material *mat = (M_Material *)m;
     568          16 :                 asp->pen_props.width = 0;
     569          16 :                 asp->fill_color = GF_COL_ARGB_FIXED(FIX_ONE, mat->diffuseColor.red, mat->diffuseColor.green, mat->diffuseColor.blue);
     570          16 :                 if (!tr_state->color_mat.identity)
     571           0 :                         asp->fill_color = gf_cmx_apply(&tr_state->color_mat, asp->fill_color);
     572             :         }
     573             :         return 0;
     574             :         default:
     575             :                 return 0;
     576             :         }
     577             : 
     578      233078 :         asp->fill_color = GF_COL_ARGB_FIXED(FIX_ONE-m->transparency, m->emissiveColor.red, m->emissiveColor.green, m->emissiveColor.blue);
     579      233078 :         if (!tr_state->color_mat.identity)
     580       24376 :                 asp->fill_color = gf_cmx_apply(&tr_state->color_mat, asp->fill_color);
     581             : 
     582      233078 :         asp->line_color = asp->fill_color;
     583      233078 :         if (!m->filled) asp->fill_color = 0;
     584             : 
     585      233078 :         if (m->lineProps == NULL) {
     586      125034 : check_default:
     587             :                 /*this is a bug in the spec: by default line width is 1.0, but in meterMetrics this means half of the screen :)*/
     588      139696 :                 asp->pen_props.width = FIX_ONE;
     589      139696 :                 if (!tr_state->pixel_metrics) asp->pen_props.width = gf_divfix(asp->pen_props.width, tr_state->min_hsize);
     590      139696 :                 if (m && m->transparency==FIX_ONE) {
     591        2169 :                         asp->pen_props.width = 0;
     592             :                 } else {
     593      137527 :                         switch (gf_node_get_tag(node)) {
     594         662 :                         case TAG_MPEG4_IndexedLineSet2D:
     595         662 :                                 asp->fill_color &= 0x00FFFFFF;
     596         662 :                                 break;
     597         296 :                         case TAG_MPEG4_PointSet2D:
     598         296 :                                 asp->fill_color |= ((u32 )(FIX2INT(255 * (m ? (FIX_ONE - m->transparency) : FIX_ONE))) ) << 24;
     599         296 :                                 asp->pen_props.width = 0;
     600         296 :                                 break;
     601      136569 :                         default:
     602      136569 :                                 if (GF_COL_A(asp->fill_color)) asp->pen_props.width = 0;
     603             :                                 /*spec is unclear about that*/
     604             :                                 //else if (!m && ctx->aspect.fill_texture) ctx->aspect.pen_props.width = 0;
     605             :                                 break;
     606             :                         }
     607             :                 }
     608             :                 return 0;
     609             :         }
     610             :         LP = NULL;
     611             :         XLP = NULL;
     612      109987 :         switch (gf_node_get_tag((GF_Node *) m->lineProps) ) {
     613       83183 :         case TAG_MPEG4_LineProperties:
     614       83183 :                 LP = (M_LineProperties *) m->lineProps;
     615       83183 :                 break;
     616       26804 :         case TAG_MPEG4_XLineProperties:
     617       26804 :                 XLP = (M_XLineProperties *) m->lineProps;
     618       26804 :                 break;
     619           0 :         default:
     620           0 :                 asp->pen_props.width = 0;
     621           0 :                 return 0;
     622             :         }
     623      109987 :         if (m->lineProps && drawable_lineprops_dirty(m->lineProps))
     624             :                 ret = CTX_APP_DIRTY;
     625             : 
     626      109987 :         if (LP) {
     627       83183 :                 asp->pen_props.dash = (u8) LP->lineStyle;
     628       83183 :                 asp->line_color = GF_COL_ARGB_FIXED(FIX_ONE-m->transparency, LP->lineColor.red, LP->lineColor.green, LP->lineColor.blue);
     629       83183 :                 asp->pen_props.width = LP->width;
     630       83183 :                 if (!tr_state->color_mat.identity) {
     631         116 :                         asp->line_color = gf_cmx_apply(&tr_state->color_mat, asp->line_color);
     632             :                 }
     633             :                 return ret;
     634             :         }
     635             : 
     636       26804 :         asp->pen_props.dash = (u8) XLP->lineStyle;
     637       26804 :         asp->line_color = GF_COL_ARGB_FIXED(FIX_ONE-XLP->transparency, XLP->lineColor.red, XLP->lineColor.green, XLP->lineColor.blue);
     638       26804 :         asp->pen_props.width = XLP->width;
     639       26804 :         if (!tr_state->color_mat.identity) {
     640         142 :                 asp->line_color = gf_cmx_apply(&tr_state->color_mat, asp->line_color);
     641             :         }
     642             : 
     643       26804 :         asp->line_scale = XLP->isScalable ? FIX_ONE : 0;
     644       26804 :         asp->pen_props.align = XLP->isCenterAligned ? GF_PATH_LINE_CENTER : GF_PATH_LINE_INSIDE;
     645       26804 :         asp->pen_props.cap = (u8) XLP->lineCap;
     646       26804 :         asp->pen_props.join = (u8) XLP->lineJoin;
     647       26804 :         asp->pen_props.miterLimit = XLP->miterLimit;
     648       26804 :         asp->pen_props.dash_offset = XLP->dashOffset;
     649             : 
     650             :         /*dash settings strutc is the same as MFFloat from XLP, typecast without storing*/
     651       26804 :         if (XLP->dashes.count) {
     652         302 :                 asp->pen_props.dash_set = (GF_DashSettings *) &XLP->dashes;
     653             :         } else {
     654       26502 :                 asp->pen_props.dash_set = NULL;
     655             :         }
     656       26804 :         asp->line_texture = gf_sc_texture_get_handler(XLP->texture);
     657       26804 :         return ret;
     658             : }
     659             : #endif /*GPAC_DISABLE_VRML*/
     660             : 
     661             : 
     662             : static Bool check_transparent_skip(DrawableContext *ctx, Bool skipFill)
     663             : {
     664             :         /*if texture, cannot skip*/
     665      152571 :         if (ctx->aspect.fill_texture) return 0;
     666      133763 :         if (! GF_COL_A(ctx->aspect.fill_color) && !GF_COL_A(ctx->aspect.line_color) ) return 1;
     667      131755 :         if (ctx->aspect.pen_props.width == 0) {
     668      106515 :                 if (skipFill) return 1;
     669      106515 :                 if (!GF_COL_A(ctx->aspect.fill_color) ) return 1;
     670             :         }
     671             :         return 0;
     672             : }
     673             : 
     674             : 
     675      178186 : void drawable_check_texture_dirty(DrawableContext *ctx, Drawable *drawable, GF_TraverseState *tr_state)
     676             : {
     677             : #ifndef GPAC_DISABLE_3D
     678             :         Bool texture_ready=0;
     679             : #endif
     680             : 
     681             :         /*Update texture info - draw even if texture not created (this may happen if the media is removed)*/
     682      178186 :         if (ctx->aspect.fill_texture) {
     683       21935 :                 if (ctx->aspect.fill_texture->needs_refresh) {
     684        6486 :                         ctx->flags |= CTX_TEXTURE_DIRTY;
     685             :                 }
     686             : #ifndef GPAC_DISABLE_3D
     687             :                 //in autoGL mode, a texture is dirty only if transparent. If not, the area covered over the video doesn't need to be repainted if unchanged
     688             :                 //disable for color matrix as we don't yet handle them in GL
     689       21935 :                 if (tr_state->visual->compositor->hybrid_opengl && !tr_state->visual->offscreen) {
     690         818 :                         u8 alpha = GF_COL_A(ctx->aspect.fill_color);
     691         818 :                         if (!alpha) alpha = GF_COL_A(ctx->aspect.line_color);
     692             : 
     693         818 :                         if (!ctx->aspect.fill_texture->transparent && (alpha==0xFF) && !ctx->aspect.fill_texture->compute_gradient_matrix && (drawable->flags & DRAWABLE_HYBGL_INIT)) {
     694         322 :                                 ctx->flags |= CTX_HYBOGL_NO_CLEAR;
     695             :                         }
     696             :                         //otherwise, we need to redraw all object below, wether they changed ot not, because we have erased this part of the canvas
     697             :                         else {
     698         496 :                                 ctx->flags |= CTX_TEXTURE_DIRTY;
     699             :                         }
     700             :                         //wait until we have something to draw to decide that the texture is ready, otherwise we will not clear the canvas when texture is ready
     701         818 :                         if (ctx->aspect.fill_texture->compute_gradient_matrix || ctx->aspect.fill_texture->data)
     702             :                                 texture_ready=1;
     703             :                 }
     704             : #endif
     705             :         }
     706             :         //same as above
     707      178186 :         if (ctx->aspect.line_texture) {
     708        1821 :                 if (ctx->aspect.line_texture->needs_refresh)
     709        1055 :                         ctx->flags |= CTX_TEXTURE_DIRTY;
     710             : 
     711             : #ifndef GPAC_DISABLE_3D
     712             :                 //in autoGL mode, a texture is dirty only if transparent. If not, the area covered over the video doesn't need to be repainted if unchanged
     713             :                 //disable for color matrix as we don't yet handle them in GL
     714        1821 :                 if (tr_state->visual->compositor->hybrid_opengl && !tr_state->visual->offscreen) {
     715           6 :                         u8 alpha = GF_COL_A(ctx->aspect.line_color);
     716           6 :                         if (!ctx->aspect.line_texture->transparent && (alpha==0xFF) && !ctx->aspect.line_texture->compute_gradient_matrix && (drawable->flags & DRAWABLE_HYBGL_INIT))
     717           0 :                                 ctx->flags |= CTX_HYBOGL_NO_CLEAR;
     718             :                         //otherwise, we need to redraw all object below, wether they changed ot not, bacause we have erased this part of the canvas
     719             :                         else
     720           6 :                                 ctx->flags |= CTX_TEXTURE_DIRTY;
     721             : 
     722             :                         //wait until we have something to draw to decide that the texture is ready, otherwise we will not clear the canvas when texture is ready
     723           6 :                         if (ctx->aspect.line_texture->compute_gradient_matrix || ctx->aspect.line_texture->data)
     724             :                                 texture_ready=1;
     725             :                 }
     726             : #endif
     727             :         }
     728             : 
     729             : #ifndef GPAC_DISABLE_3D
     730             :         //from now on, we won't clear the canvas when updating this texture (unless transparent, cf above)
     731      178186 :         if (texture_ready)
     732         492 :                 drawable->flags |= DRAWABLE_HYBGL_INIT;
     733             : #endif
     734      178186 : }
     735             : 
     736             : #ifndef GPAC_DISABLE_VRML
     737             : 
     738      152571 : DrawableContext *drawable_init_context_mpeg4(Drawable *drawable, GF_TraverseState *tr_state)
     739             : {
     740             :         DrawableContext *ctx;
     741             :         Bool skipFill;
     742             :         GF_Node *appear;
     743             :         assert(tr_state->visual);
     744             : 
     745             :         /*switched-off geometry nodes are not drawn*/
     746      152571 :         if (tr_state->switched_off) {
     747           0 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Drawable is switched off - skipping\n"));
     748             :                 return NULL;
     749             :         }
     750             : 
     751             :         //Get a empty context from the current visual
     752      152571 :         ctx = visual_2d_get_drawable_context(tr_state->visual);
     753      152571 :         if (!ctx) return NULL;
     754             : 
     755      152571 :         ctx->drawable = drawable;
     756             : 
     757      152571 :         appear = tr_state->override_appearance ? tr_state->override_appearance : tr_state->appear;
     758             : 
     759             :         /*usually set by colorTransform or changes in OrderedGroup*/
     760      152571 :         if (tr_state->invalidate_all)
     761        1615 :                 ctx->flags |= CTX_APP_DIRTY;
     762             : 
     763      152571 :         ctx->aspect.fill_texture = NULL;
     764      152571 :         if (appear) {
     765      150923 :                 ctx->appear = appear;
     766      150923 :                 if (gf_node_dirty_get(appear))
     767       51339 :                         ctx->flags |= CTX_APP_DIRTY;
     768             :         }
     769             :         /*todo cliper*/
     770             : 
     771             :         /*FIXME - only needed for texture*/
     772      152571 :         if (!tr_state->color_mat.identity) {
     773        5285 :                 GF_SAFEALLOC(ctx->col_mat, GF_ColorMatrix);
     774        5285 :                 if (ctx->col_mat)
     775        5285 :                         gf_cmx_copy(ctx->col_mat, &tr_state->color_mat);
     776             :         }
     777             : 
     778             :         /*IndexedLineSet2D and PointSet2D ignores fill flag and texturing*/
     779             :         skipFill = 0;
     780      152571 :         ctx->aspect.fill_texture = NULL;
     781      152571 :         switch (gf_node_get_tag(ctx->drawable->node) ) {
     782             : #ifndef GPAC_DISABLE_VRML
     783         904 :         case TAG_MPEG4_IndexedLineSet2D:
     784             :                 skipFill = 1;
     785         904 :                 break;
     786             : #endif
     787             :         default:
     788             :                 break;
     789             :         }
     790             : 
     791      152571 :         ctx->flags |= drawable_get_aspect_2d_mpeg4(drawable->node, &ctx->aspect, tr_state);
     792             : 
     793      152571 :         drawable_check_texture_dirty(ctx, drawable, tr_state);
     794             : 
     795             :         /*not clear in the spec: what happens when a transparent node is in form/layout ?? this may
     796             :         completely break layout of children. We consider the node should be drawn*/
     797      152571 :         if (!tr_state->parent && check_transparent_skip(ctx, skipFill)) {
     798       36214 :                 visual_2d_remove_last_context(tr_state->visual);
     799       36214 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Drawable is fully transparent - skipping\n"));
     800             :                 return NULL;
     801             :         }
     802      116357 :         ctx->flags |= CTX_HAS_APPEARANCE;
     803             : 
     804             :         /*we are drawing on a fliped coord surface, remember to flip the texture*/
     805      116357 :         if (tr_state->fliped_coords)
     806         341 :                 ctx->flags |= CTX_FLIPED_COORDS;
     807             : 
     808             : #ifdef GF_SR_USE_DEPTH
     809      116357 :         ctx->depth_gain=tr_state->depth_gain;
     810      116357 :         ctx->depth_offset=tr_state->depth_offset;
     811             : #endif
     812             : 
     813      116357 :         return ctx;
     814             : }
     815             : #endif
     816             : 
     817      141466 : static Bool drawable_finalize_end(struct _drawable_context *ctx, GF_TraverseState *tr_state)
     818             : {
     819             :         /*if direct draw we can remove the context*/
     820      141466 :         Bool res = tr_state->immediate_draw ? 1 : 0;
     821             :         /*copy final transform, once all parent layout is done*/
     822      141466 :         gf_mx2d_copy(ctx->transform, tr_state->transform);
     823             : 
     824             :         /*setup clipper and register bounds & sensors*/
     825      141466 :         gf_irect_intersect(&ctx->bi->clip, &tr_state->visual->top_clipper);
     826      141466 :         if (!ctx->bi->clip.width || !ctx->bi->clip.height) {
     827        6017 :                 ctx->bi->clip.width = 0;
     828             :                 /*remove if this is the last context*/
     829        6017 :                 if (tr_state->visual->cur_context == ctx) tr_state->visual->cur_context->drawable = NULL;
     830             : 
     831             :                 return res;
     832             :         }
     833             : 
     834             :         /*keep track of node drawn, whether direct or indirect drawing*/
     835      135449 :         if (!(ctx->drawable->flags & DRAWABLE_REGISTERED_WITH_VISUAL) ) {
     836             :                 struct _drawable_store *it;
     837        5036 :                 GF_SAFEALLOC(it, struct _drawable_store);
     838        5036 :                 if (!it) {
     839             :                         return 0;
     840             :                 }
     841        5036 :                 it->drawable = ctx->drawable;
     842        5036 :                 if (tr_state->visual->last_prev_entry) {
     843        4480 :                         tr_state->visual->last_prev_entry->next = it;
     844        4480 :                         tr_state->visual->last_prev_entry = it;
     845             :                 } else {
     846         556 :                         tr_state->visual->prev_nodes = tr_state->visual->last_prev_entry = it;
     847             :                 }
     848             :                 //GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor2D] Registering new drawn node %s on visual\n", gf_node_get_class_name(it->drawable->node)));
     849        5036 :                 ctx->drawable->flags |= DRAWABLE_REGISTERED_WITH_VISUAL;
     850             :         }
     851             : 
     852             :         /*we are in direct draw mode, draw ...*/
     853      135449 :         if (res) {
     854             :                 /*if over an overlay we cannot remove the context and cannot draw directly*/
     855        5242 :                 if (visual_2d_overlaps_overlay(tr_state->visual, ctx, tr_state))
     856             :                         return 0;
     857             : 
     858             :                 assert(!tr_state->traversing_mode);
     859        5242 :                 tr_state->traversing_mode = TRAVERSE_DRAW_2D;
     860        5242 :                 tr_state->ctx = ctx;
     861             : 
     862        5242 :                 if (ctx->drawable->flags & DRAWABLE_USE_TRAVERSE_DRAW) {
     863        3484 :                         gf_node_allow_cyclic_traverse(ctx->drawable->node);
     864        3484 :                         gf_node_traverse(ctx->drawable->node, tr_state);
     865             :                 } else {
     866        1758 :                         drawable_draw(ctx->drawable, tr_state);
     867             :                 }
     868             : 
     869        5242 :                 tr_state->ctx = NULL;
     870        5242 :                 tr_state->traversing_mode = TRAVERSE_SORT;
     871             :         }
     872             :         /*if the drawable is an overlay, always mark it as dirty to avoid flickering*/
     873      130207 :         else if (ctx->drawable->flags & DRAWABLE_IS_OVERLAY) {
     874           0 :                 ctx->flags |= CTX_APP_DIRTY;
     875             :         }
     876             :         /*if direct draw we can remove the context*/
     877             :         return res;
     878             : }
     879             : 
     880       14130 : void drawable_check_bounds(struct _drawable_context *ctx, GF_VisualManager *visual)
     881             : {
     882      155596 :         if (!ctx->bi) {
     883      142320 :                 ctx->bi = drawable_check_alloc_bounds(ctx, visual);
     884             :                 assert(ctx->bi);
     885      142320 :                 ctx->bi->extra_check = ctx->appear;
     886             :         }
     887       14130 : }
     888             : 
     889       26502 : void drawable_compute_line_scale(GF_TraverseState *tr_state, DrawAspect2D *asp)
     890             : {
     891             :         GF_Rect rc;
     892       26502 :         rc.x = rc.y = 0;
     893       26502 :         rc.width = rc.height = FIX_ONE;
     894             : #ifndef GPAC_DISABLE_3D
     895       26502 :         if (tr_state->visual->type_3d)
     896        1350 :                 gf_mx_apply_rect(&tr_state->model_matrix, &rc);
     897             :         else
     898             : #endif
     899       25152 :                 gf_mx2d_apply_rect(&tr_state->transform, &rc);
     900             : 
     901       26502 :         asp->line_scale = MAX(gf_divfix(tr_state->visual->compositor->scale_x, rc.width), gf_divfix(tr_state->visual->compositor->scale_y, rc.height));
     902       26502 : }
     903             : 
     904             : //#define REMOVE_UNUSED_CTX
     905      141466 : void drawable_finalize_sort_ex(DrawableContext *ctx, GF_TraverseState *tr_state, GF_Rect *orig_bounds, Bool skip_focus)
     906             : {
     907             : #ifdef REMVE_UNUSED_CTX
     908             :         Bool can_remove = 0;
     909             : #endif
     910             :         Fixed pw;
     911             :         GF_Rect unclip, store_orig_bounds;
     912      141466 :         GF_Node *appear = tr_state->override_appearance ? tr_state->override_appearance : tr_state->appear;
     913             : 
     914             : 
     915      141466 :         drawable_check_bounds(ctx, tr_state->visual);
     916             : 
     917      141466 :         if (orig_bounds) {
     918       45939 :                 store_orig_bounds = *orig_bounds;
     919             :         } else {
     920       95527 :                 gf_path_get_bounds(ctx->drawable->path, &store_orig_bounds);
     921             :         }
     922      141466 :         ctx->bi->unclip = store_orig_bounds;
     923      141466 :         gf_mx2d_apply_rect(&tr_state->transform, &ctx->bi->unclip);
     924             : 
     925             :         /*apply pen width*/
     926      141466 :         if (ctx->aspect.pen_props.width) {
     927             :                 StrikeInfo2D *si = NULL;
     928             : 
     929       37385 :                 if (!ctx->aspect.line_scale)
     930       25141 :                         drawable_compute_line_scale(tr_state, &ctx->aspect);
     931             : 
     932             : #if 0
     933             :                 /*if pen is not scalable, apply user/viewport transform so that original aspect is kept*/
     934             :                 if (!ctx->aspect.line_scale) {
     935             :                         GF_Point2D pt;
     936             :                         pt.x = tr_state->transform.m[0] + tr_state->transform.m[1];
     937             :                         pt.y = tr_state->transform.m[3] + tr_state->transform.m[4];
     938             :                         ctx->aspect.line_scale = gf_divfix(FLT2FIX(1.41421356f) , gf_v2d_len(&pt));
     939             :                 }
     940             : #endif
     941             : 
     942             :                 /*get strike info & outline for exact bounds compute. If failure use default offset*/
     943       37385 :                 si = drawable_get_strikeinfo(tr_state->visual->compositor, ctx->drawable, &ctx->aspect, appear, ctx->drawable->path, ctx->flags, NULL);
     944       37385 :                 if (si && si->outline) {
     945       37078 :                         gf_path_get_bounds(si->outline, &ctx->bi->unclip);
     946       37078 :                         gf_mx2d_apply_rect(&tr_state->transform, &ctx->bi->unclip);
     947             :                 } else {
     948         307 :                         pw = gf_mulfix(ctx->aspect.pen_props.width, ctx->aspect.line_scale);
     949         307 :                         ctx->bi->unclip.x -= pw/2;
     950         307 :                         ctx->bi->unclip.y += pw/2;
     951         307 :                         ctx->bi->unclip.width += pw;
     952         307 :                         ctx->bi->unclip.height += pw;
     953             :                 }
     954             :         }
     955             : 
     956      141466 :         if (ctx->bi->unclip.width && ctx->bi->unclip.height) {
     957      136520 :                 unclip = ctx->bi->unclip;
     958      136520 :                 if (! (ctx->flags & CTX_NO_ANTIALIAS)) {
     959             :                         /*grow of 2 pixels (-1 and +1) to handle AA, but ONLY on cliper otherwise we will modify layout/form */
     960      113342 :                         pw = (tr_state->pixel_metrics) ? FIX_ONE : 2*FIX_ONE/tr_state->visual->width;
     961      113342 :                         unclip.x -= pw;
     962      113342 :                         unclip.y += pw;
     963      113342 :                         unclip.width += 2*pw;
     964      113342 :                         unclip.height += 2*pw;
     965             :                 }
     966      136520 :                 ctx->bi->clip = gf_rect_pixelize(&unclip);
     967             :         } else {
     968        4946 :                 ctx->bi->clip.width = 0;
     969             :         }
     970             : 
     971             : #ifdef REMVE_UNUSED_CTX
     972             :         can_remove = drawable_finalize_end(ctx, tr_state);
     973             : #else
     974      141466 :         drawable_finalize_end(ctx, tr_state);
     975             : #endif
     976      141466 :         if (ctx->drawable && !skip_focus)
     977      135145 :                 drawable_check_focus_highlight(ctx->drawable->node, tr_state, &store_orig_bounds);
     978             : 
     979             :         /*remove if this is the last context*/
     980             : #ifdef REMVE_UNUSED_CTX
     981             :         if (can_remove && (tr_state->visual->cur_context == ctx))
     982             :                 tr_state->visual->cur_context->drawable = NULL;
     983             : #endif
     984      141466 : }
     985             : 
     986      141162 : void drawable_finalize_sort(struct _drawable_context *ctx, GF_TraverseState *tr_state, GF_Rect *orig_bounds)
     987             : {
     988      141162 :         drawable_finalize_sort_ex(ctx, tr_state, orig_bounds, 0);
     989      141162 : }
     990             : 
     991             : 
     992      372878 : void drawable_check_focus_highlight(GF_Node *node, GF_TraverseState *tr_state, GF_Rect *orig_bounds)
     993             : {
     994             :         DrawableContext *hl_ctx;
     995             :         Drawable *hlight;
     996             :         GF_Node *prev_node;
     997             :         u32 prev_mode;
     998             :         GF_Matrix2D cur;
     999      372878 :         GF_Compositor *compositor = tr_state->visual->compositor;
    1000             : 
    1001      745452 :         if (compositor->disable_focus_highlight) return;
    1002             : 
    1003      372878 :         if (compositor->focus_node!=node) return;
    1004             :         /*if key navigator, don't draw a focus highlight*/
    1005         327 :         if (compositor->keynav_node) return;
    1006             : 
    1007         304 :         if (compositor->focus_used) {
    1008           0 :                 u32 count = gf_list_count(tr_state->use_stack);
    1009           0 :                 if (!count || (gf_list_get(tr_state->use_stack, count-1)!=compositor->focus_used) )
    1010             :                         return;
    1011             :         }
    1012             : 
    1013         304 :         hlight = compositor->focus_highlight;
    1014         304 :         if (!hlight) return;
    1015             : 
    1016             :         /*check if focus node has changed*/
    1017         304 :         prev_node = gf_node_get_private(hlight->node);
    1018         304 :         if (prev_node != node) {
    1019             :                 GF_Rect *bounds;
    1020             :                 /*this is a grouping node, get its bounds*/
    1021          83 :                 if (!orig_bounds) {
    1022          83 :                         gf_mx2d_copy(cur, tr_state->transform);
    1023          83 :                         gf_mx2d_init(tr_state->transform);
    1024          83 :                         prev_mode = tr_state->traversing_mode;
    1025          83 :                         tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
    1026          83 :                         tr_state->bounds.width = tr_state->bounds.height = 0;
    1027          83 :                         tr_state->bounds.x = tr_state->bounds.y = 0;
    1028             : 
    1029          83 :                         gf_sc_get_nodes_bounds(node, ((GF_ParentNode *)node)->children, tr_state, NULL);
    1030             : 
    1031          83 :                         tr_state->traversing_mode = prev_mode;
    1032             :                         gf_mx2d_copy(tr_state->transform, cur);
    1033          83 :                         bounds = &tr_state->bounds;
    1034             :                 } else {
    1035             :                         bounds = orig_bounds;
    1036             :                 }
    1037          83 :                 gf_node_set_private(hlight->node, node);
    1038             : 
    1039          83 :                 drawable_reset_path(hlight);
    1040          83 :                 gf_path_reset(hlight->path);
    1041          83 :                 gf_path_add_rect(hlight->path, bounds->x, bounds->y, bounds->width, bounds->height);
    1042             :         }
    1043         304 :         hl_ctx = visual_2d_get_drawable_context(tr_state->visual);
    1044         304 :         hl_ctx->drawable = hlight;
    1045         304 :         hl_ctx->aspect.fill_color = compositor->hlfill;
    1046         304 :         hl_ctx->aspect.line_color = compositor->hlline;
    1047         304 :         hl_ctx->aspect.line_scale = 0;
    1048         304 :         hl_ctx->aspect.pen_props.width = compositor->hllinew;
    1049         304 :         hl_ctx->aspect.pen_props.join = GF_LINE_JOIN_BEVEL;
    1050         304 :         hl_ctx->aspect.pen_props.dash = GF_DASH_STYLE_DOT;
    1051             : 
    1052             :         /*editing this node - move to solid stroke*/
    1053         304 :         if (compositor->edited_text) {
    1054           0 :                 hl_ctx->aspect.pen_props.width = 2*FIX_ONE;
    1055           0 :                 hl_ctx->aspect.pen_props.dash = 1;
    1056           0 :                 hl_ctx->aspect.line_color = compositor->hlline;
    1057             :         }
    1058             : 
    1059             : 
    1060             : #ifndef GPAC_DISABLE_3D
    1061         304 :         if (tr_state->visual->type_3d) {
    1062           0 :                 gf_mx2d_copy(hl_ctx->transform, tr_state->transform);
    1063           0 :                 visual_3d_draw_2d_with_aspect(hl_ctx->drawable, tr_state, &hl_ctx->aspect);
    1064           0 :                 return;
    1065             :         }
    1066             : #endif
    1067         304 :         gf_mx2d_copy(hl_ctx->transform, tr_state->transform);
    1068         304 :         drawable_finalize_sort_ex(hl_ctx, tr_state, NULL, 1);
    1069             : }
    1070             : 
    1071             : 
    1072         604 : void drawable_traverse_focus(GF_Node *node, void *rs, Bool is_destroy)
    1073             : {
    1074             :         GF_TraverseState *tr_state = (GF_TraverseState *)rs;
    1075         604 :         if (is_destroy) return;
    1076           0 :         if (tr_state->traversing_mode == TRAVERSE_DRAW_2D)
    1077           0 :                 visual_2d_draw_path(tr_state->visual, tr_state->ctx->drawable->path, tr_state->ctx, NULL, NULL, tr_state);
    1078             : }
    1079             : 
    1080             : 
    1081        1609 : void delete_strikeinfo2d(StrikeInfo2D *info)
    1082             : {
    1083        1609 :         if (info->outline) gf_path_del(info->outline);
    1084             : #ifndef GPAC_DISABLE_3D
    1085        1609 :         if (info->mesh_outline) mesh_free(info->mesh_outline);
    1086             : #endif
    1087        1609 :         gf_free(info);
    1088        1609 : }
    1089             : 
    1090             : 
    1091       99035 : StrikeInfo2D *drawable_get_strikeinfo(GF_Compositor *compositor, Drawable *drawable, DrawAspect2D *asp, GF_Node *appear, GF_Path *path, u32 svg_flags, GF_TraverseState *tr_state)
    1092             : {
    1093             :         StrikeInfo2D *si, *prev;
    1094             :         GF_Node *lp;
    1095             : #ifndef GPAC_DISABLE_VRML
    1096             :         Bool dirty;
    1097             : #endif
    1098       99035 :         if (!asp->pen_props.width) return NULL;
    1099       99035 :         if (path && !path->n_points) return NULL;
    1100             : 
    1101             :         lp = NULL;
    1102             : #ifndef GPAC_DISABLE_VRML
    1103       98421 :         if (appear && (gf_node_get_tag(appear) < GF_NODE_RANGE_LAST_X3D) ) {
    1104       85537 :                 lp = ((M_Appearance *)appear)->material;
    1105       85537 :                 if (lp) lp = ((M_Material2D *) lp)->lineProps;
    1106             :         }
    1107             : #endif
    1108             : 
    1109             :         prev = NULL;
    1110       98421 :         si = drawable->outline;
    1111      203889 :         while (si) {
    1112             :                 /*note this includes default LP (NULL)*/
    1113       98522 :                 if ((si->lineProps == lp) && (!path || (path==si->original)) ) break;
    1114        7047 :                 if (!si->lineProps) {
    1115        5336 :                         gf_list_del_item(compositor->strike_bank, si);
    1116        5336 :                         if (si->outline) gf_path_del(si->outline);
    1117             : #ifndef GPAC_DISABLE_3D
    1118        5336 :                         if (si->mesh_outline) mesh_free(si->mesh_outline);
    1119             : #endif
    1120        5336 :                         if (prev) prev->next = si->next;
    1121        5329 :                         else drawable->outline = si->next;
    1122        5336 :                         gf_free(si);
    1123        5336 :                         si = prev ? prev->next : drawable->outline;
    1124        5336 :                         continue;
    1125             :                 }
    1126             :                 prev = si;
    1127        1711 :                 si = si->next;
    1128             :         }
    1129             :         /*not found, add*/
    1130       98421 :         if (!si) {
    1131        6946 :                 GF_SAFEALLOC(si, StrikeInfo2D);
    1132        6946 :                 if (!si) {
    1133             :                         return NULL;
    1134             :                 }
    1135        6946 :                 si->lineProps = lp;
    1136        6946 :                 si->drawable = drawable;
    1137             : 
    1138        6946 :                 if (drawable->outline) {
    1139             :                         prev = drawable->outline;
    1140         132 :                         while (prev->next) prev = prev->next;
    1141          78 :                         prev->next = si;
    1142             :                 } else {
    1143        6868 :                         drawable->outline = si;
    1144             :                 }
    1145        6946 :                 gf_list_add(compositor->strike_bank, si);
    1146             :         }
    1147             : 
    1148             :         /*3D drawing of outlines*/
    1149             : #ifndef GPAC_DISABLE_3D
    1150       98421 :         if (tr_state && !asp->line_scale) {
    1151        1214 :                 drawable_compute_line_scale(tr_state, asp);
    1152             :         }
    1153             : #endif
    1154             : 
    1155             :         /*picking*/
    1156       98421 :         if (!asp->line_scale) return si;
    1157             : 
    1158             :         /*node changed or outline not build*/
    1159             : #ifndef GPAC_DISABLE_VRML
    1160       95222 :         dirty = lp ? drawable_lineprops_dirty(lp) : 0;
    1161             : #endif
    1162             : 
    1163       95222 :         if (!si->outline
    1164             : #ifndef GPAC_DISABLE_VRML
    1165       88268 :                 || dirty
    1166             : #endif
    1167       84677 :                 || (si->line_scale != asp->line_scale) || (si->path_length != asp->pen_props.path_length) || (svg_flags & CTX_SVG_OUTLINE_GEOMETRY_DIRTY)) {
    1168             :                 u32 i;
    1169       13639 :                 Fixed w = asp->pen_props.width;
    1170       13639 :                 Fixed dash_o = asp->pen_props.dash_offset;
    1171       13639 :                 si->line_scale = asp->line_scale;
    1172       13639 :                 if (si->outline) gf_path_del(si->outline);
    1173             : #ifndef GPAC_DISABLE_3D
    1174       13639 :                 if (si->mesh_outline) {
    1175         614 :                         mesh_free(si->mesh_outline);
    1176         614 :                         si->mesh_outline = NULL;
    1177             :                 }
    1178             : #endif
    1179             :                 /*apply scale whether scalable or not (if not scalable, scale is still needed for scalable zoom)*/
    1180       13639 :                 asp->pen_props.width = gf_mulfix(asp->pen_props.width, asp->line_scale);
    1181       13639 :                 if (asp->pen_props.dash != GF_DASH_STYLE_SVG)
    1182       13639 :                         asp->pen_props.dash_offset = gf_mulfix(asp->pen_props.dash_offset, asp->pen_props.width);
    1183             : 
    1184       13639 :                 if (asp->pen_props.dash_set) {
    1185        1192 :                         for(i=0; i<asp->pen_props.dash_set->num_dash; i++) {
    1186        1192 :                                 asp->pen_props.dash_set->dashes[i] = gf_mulfix(asp->pen_props.dash_set->dashes[i], asp->line_scale);
    1187             :                         }
    1188             :                 }
    1189             : 
    1190       13639 :                 if (path) {
    1191       12886 :                         si->outline = gf_path_get_outline(path, asp->pen_props);
    1192       12886 :                         si->original = path;
    1193             :                 } else {
    1194         753 :                         si->outline = gf_path_get_outline(drawable->path, asp->pen_props);
    1195             :                 }
    1196             :                 /*restore*/
    1197       13639 :                 asp->pen_props.width = w;
    1198       13639 :                 asp->pen_props.dash_offset = dash_o;
    1199       13639 :                 if (asp->pen_props.dash_set) {
    1200        1192 :                         for(i=0; i<asp->pen_props.dash_set->num_dash; i++) {
    1201        1192 :                                 asp->pen_props.dash_set->dashes[i] = gf_divfix(asp->pen_props.dash_set->dashes[i], asp->line_scale);
    1202             :                         }
    1203             :                 }
    1204             :         }
    1205             : 
    1206             :         return si;
    1207             : }
    1208             : 
    1209        9933 : void drawable_reset_path_outline(Drawable *st)
    1210             : {
    1211        9933 :         StrikeInfo2D *si = st->outline;
    1212       20429 :         while (si) {
    1213         563 :                 if (si->outline) gf_path_del(si->outline);
    1214         563 :                 si->outline = NULL;
    1215             : #ifndef GPAC_DISABLE_3D
    1216         563 :                 if (si->mesh_outline) mesh_free(si->mesh_outline);
    1217         563 :                 si->mesh_outline = NULL;
    1218             : #endif
    1219         563 :                 si->original = NULL;
    1220         563 :                 si = si->next;
    1221             :         }
    1222             : #ifndef GPAC_DISABLE_3D
    1223        9933 :         if (st->mesh) {
    1224         174 :                 mesh_free(st->mesh);
    1225         174 :                 st->mesh = NULL;
    1226             :         }
    1227             : #endif
    1228        9933 : }
    1229             : 
    1230        9010 : void drawable_reset_path(Drawable *st)
    1231             : {
    1232        9010 :         drawable_reset_path_outline(st);
    1233        9010 :         if (st->path) gf_path_reset(st->path);
    1234             : #ifndef GPAC_DISABLE_3D
    1235        9010 :         if (st->mesh) {
    1236           0 :                 mesh_free(st->mesh);
    1237           0 :                 st->mesh = NULL;
    1238             :         }
    1239             : #endif
    1240        9010 : }
    1241             : 
    1242             : #ifndef GPAC_DISABLE_VRML
    1243             : 
    1244         983 : static void DestroyLineProps(GF_Node *n, void *rs, Bool is_destroy)
    1245             : {
    1246             :         StrikeInfo2D *si, *cur, *prev;
    1247             :         u32 i;
    1248             :         LinePropStack *st;
    1249         983 :         if (!is_destroy) return;
    1250             : 
    1251         983 :         st = (LinePropStack *)gf_node_get_private(n);
    1252         983 :         i = 0;
    1253             : 
    1254       86528 :         while ((si = (StrikeInfo2D*)gf_list_enum(st->compositor->strike_bank, &i))) {
    1255       84562 :                 if (si->lineProps == n) {
    1256             :                         /*remove from node*/
    1257         576 :                         if (si->drawable) {
    1258             :                                 assert(si->drawable->outline);
    1259         576 :                                 cur = si->drawable->outline;
    1260             :                                 prev = NULL;
    1261        1176 :                                 while (cur) {
    1262         600 :                                         if (cur!=si) {
    1263             :                                                 prev = cur;
    1264          24 :                                                 cur = cur->next;
    1265          24 :                                                 continue;
    1266             :                                         }
    1267         576 :                                         if (prev) prev->next = cur->next;
    1268         562 :                                         else si->drawable->outline = cur->next;
    1269             :                                         break;
    1270             :                                 }
    1271             :                         }
    1272         576 :                         i--;
    1273         576 :                         gf_list_rem(st->compositor->strike_bank, i);
    1274         576 :                         delete_strikeinfo2d(si);
    1275             :                 }
    1276             :         }
    1277             : 
    1278         983 :         gf_free(st);
    1279             : 
    1280             : }
    1281             : 
    1282         983 : void compositor_init_lineprops(GF_Compositor *compositor, GF_Node *node)
    1283             : {
    1284             :         LinePropStack *st;
    1285         983 :         GF_SAFEALLOC(st, LinePropStack);
    1286         983 :         if (!st) {
    1287           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate line properties stack\n"));
    1288             :                 return;
    1289             :         }
    1290         983 :         st->compositor = compositor;
    1291         983 :         st->last_mod_time = 0;
    1292         983 :         gf_node_set_private(node, st);
    1293         983 :         gf_node_set_callback_function(node, DestroyLineProps);
    1294             : }
    1295             : 
    1296             : #endif /*GPAC_DISABLE_VRML*/
    1297             : 
    1298             : 
    1299             : #ifdef GPAC_DISABLE_SVG
    1300             : 
    1301             : Bool drawable_get_aspect_2d_svg(GF_Node *node, DrawAspect2D *asp, GF_TraverseState *tr_state)
    1302             : {
    1303             :         return 0;
    1304             : }
    1305             : 
    1306             : #else
    1307             : 
    1308       25443 : Bool drawable_get_aspect_2d_svg(GF_Node *node, DrawAspect2D *asp, GF_TraverseState *tr_state)
    1309             : {
    1310             :         Bool ret = 0;
    1311       25443 :         SVGPropertiesPointers *props = tr_state->svg_props;
    1312             :         Fixed clamped_opacity = FIX_ONE;
    1313             :         Fixed clamped_solid_opacity = FIX_ONE;
    1314       25443 :         Fixed clamped_fill_opacity = (props->fill_opacity->value < 0 ? 0 : (props->fill_opacity->value > FIX_ONE ? FIX_ONE : props->fill_opacity->value));
    1315       25443 :         Fixed clamped_stroke_opacity = (props->stroke_opacity->value < 0 ? 0 : (props->stroke_opacity->value > FIX_ONE ? FIX_ONE : props->stroke_opacity->value));
    1316             : 
    1317       25443 :         if (props->opacity) {
    1318         315 :                 clamped_opacity = (props->opacity->value < 0 ? 0 : (props->opacity->value > FIX_ONE ? FIX_ONE : props->opacity->value));
    1319         315 :                 if (clamped_opacity!=FIX_ONE) {
    1320         315 :                         clamped_fill_opacity = gf_mulfix(clamped_fill_opacity, clamped_opacity);
    1321         315 :                         clamped_stroke_opacity = gf_mulfix(clamped_stroke_opacity, clamped_opacity);
    1322             :                 }
    1323             :         }
    1324       25443 :         asp->fill_color = 0;
    1325             : 
    1326       25443 :         if (props->fill->type==SVG_PAINT_URI) {
    1327          62 :                 if (props->fill->iri.type != XMLRI_ELEMENTID) {
    1328             :                         /* trying to resolve the IRI to the Paint Server */
    1329           0 :                         XMLRI *iri = &props->fill->iri;
    1330           0 :                         GF_SceneGraph *sg = gf_node_get_graph(node);
    1331           0 :                         GF_Node *n = gf_sg_find_node_by_name(sg, &(iri->string[1]));
    1332           0 :                         if (n) {
    1333           0 :                                 iri->type = XMLRI_ELEMENTID;
    1334           0 :                                 iri->target = n;
    1335           0 :                                 gf_node_register_iri(sg, iri);
    1336           0 :                                 gf_free(iri->string);
    1337           0 :                                 iri->string = NULL;
    1338             :                         }
    1339             :                 }
    1340             :                 /* If paint server not found, paint is equivalent to none */
    1341          62 :                 if (props->fill->iri.type == XMLRI_ELEMENTID) {
    1342          62 :                         asp->fill_color = GF_COL_ARGB_FIXED(clamped_opacity, 0, 0, 0);
    1343          62 :                         switch (gf_node_get_tag((GF_Node *)props->fill->iri.target)) {
    1344           2 :                         case TAG_SVG_solidColor:
    1345             :                         {
    1346             :                                 SVGAllAttributes all_atts;
    1347           2 :                                 gf_svg_flatten_attributes((SVG_Element*)props->fill->iri.target, &all_atts);
    1348             : 
    1349           2 :                                 gf_node_traverse(props->fill->iri.target, tr_state);
    1350             : 
    1351           2 :                                 ret += compositor_svg_solid_color_dirty(tr_state->visual->compositor, props->fill->iri.target);
    1352             : 
    1353           2 :                                 if (all_atts.solid_color) {
    1354           2 :                                         if (all_atts.solid_opacity) {
    1355           0 :                                                 Fixed val = all_atts.solid_opacity->value;
    1356           0 :                                                 clamped_solid_opacity = MIN(FIX_ONE, MAX(0, val) );
    1357           0 :                                                 clamped_solid_opacity = gf_mulfix(clamped_solid_opacity, clamped_opacity);
    1358             :                                         }
    1359           2 :                                         asp->fill_color = GF_COL_ARGB_FIXED(clamped_solid_opacity, all_atts.solid_color->color.red, all_atts.solid_color->color.green, all_atts.solid_color->color.blue);
    1360             :                                 }
    1361             :                         }
    1362           2 :                         break;
    1363          60 :                         case TAG_SVG_linearGradient:
    1364             :                         case TAG_SVG_radialGradient:
    1365          60 :                                 asp->fill_texture = gf_sc_texture_get_handler((GF_Node *)props->fill->iri.target);
    1366          60 :                                 break;
    1367             :                         /*FIXME*/
    1368             :                         default:
    1369             :                                 break;
    1370             :                         }
    1371           0 :                 }
    1372       25381 :         } else if (props->fill->type == SVG_PAINT_COLOR) {
    1373       23233 :                 if (props->fill->color.type == SVG_COLOR_CURRENTCOLOR) {
    1374           0 :                         asp->fill_color = GF_COL_ARGB_FIXED(clamped_fill_opacity, props->color->color.red, props->color->color.green, props->color->color.blue);
    1375       23233 :                 } else if (props->fill->color.type == SVG_COLOR_RGBCOLOR) {
    1376       23233 :                         asp->fill_color = GF_COL_ARGB_FIXED(clamped_fill_opacity, props->fill->color.red, props->fill->color.green, props->fill->color.blue);
    1377           0 :                 } else if (props->fill->color.type >= SVG_COLOR_ACTIVE_BORDER) {
    1378           0 :                         asp->fill_color = tr_state->visual->compositor->sys_colors[props->fill->color.type - 3];
    1379           0 :                         asp->fill_color |= ((u32) (clamped_fill_opacity*255) ) << 24;
    1380             :                 }
    1381             :         }
    1382       25443 :         if (!tr_state->color_mat.identity)
    1383           0 :                 asp->fill_color = gf_cmx_apply(&tr_state->color_mat, asp->fill_color);
    1384             : 
    1385       25443 :         asp->line_color = 0;
    1386       25443 :         asp->pen_props.width = (props->stroke->type != SVG_PAINT_NONE) ? props->stroke_width->value : 0;
    1387       25443 :         if (props->stroke->type==SVG_PAINT_URI) {
    1388         154 :                 if (props->stroke->iri.type != XMLRI_ELEMENTID) {
    1389             :                         /* trying to resolve the IRI to the Paint Server */
    1390           0 :                         XMLRI *iri = &props->stroke->iri;
    1391           0 :                         GF_SceneGraph *sg = gf_node_get_graph(node);
    1392           0 :                         GF_Node *n = gf_sg_find_node_by_name(sg, &(iri->string[1]));
    1393           0 :                         if (n) {
    1394           0 :                                 iri->type = XMLRI_ELEMENTID;
    1395           0 :                                 iri->target = n;
    1396           0 :                                 gf_node_register_iri(sg, iri);
    1397           0 :                                 gf_free(iri->string);
    1398           0 :                                 iri->string = NULL;
    1399             :                         }
    1400             :                 }
    1401             :                 /* Paint server not found, stroke is equivalent to none */
    1402         308 :                 if ((props->stroke->iri.type == XMLRI_ELEMENTID) && props->stroke->iri.target) {
    1403         154 :                         switch (gf_node_get_tag((GF_Node *)props->stroke->iri.target)) {
    1404           4 :                         case TAG_SVG_solidColor:
    1405             :                         {
    1406             :                                 SVGAllAttributes all_atts;
    1407           4 :                                 gf_svg_flatten_attributes((SVG_Element*)props->stroke->iri.target, &all_atts);
    1408             : 
    1409           4 :                                 gf_node_traverse(props->stroke->iri.target, tr_state);
    1410             : 
    1411           4 :                                 ret += compositor_svg_solid_color_dirty(tr_state->visual->compositor, props->stroke->iri.target);
    1412             : 
    1413           4 :                                 if (all_atts.solid_color) {
    1414           4 :                                         if (all_atts.solid_opacity) {
    1415           0 :                                                 Fixed val = all_atts.solid_opacity->value;
    1416           0 :                                                 clamped_solid_opacity = MIN(FIX_ONE, MAX(0, val) );
    1417             :                                         }
    1418           4 :                                         asp->line_color = GF_COL_ARGB_FIXED(clamped_solid_opacity, all_atts.solid_color->color.red, all_atts.solid_color->color.green, all_atts.solid_color->color.blue);
    1419             :                                 }
    1420             :                         }
    1421           4 :                         break;
    1422         150 :                         case TAG_SVG_linearGradient:
    1423             :                         case TAG_SVG_radialGradient:
    1424         150 :                                 asp->line_texture = gf_sc_texture_get_handler((GF_Node *)props->stroke->iri.target);
    1425         150 :                                 break;
    1426             :                         default:
    1427             :                                 break;
    1428             :                         }
    1429           0 :                 }
    1430       25289 :         } else if (props->stroke->type == SVG_PAINT_COLOR) {
    1431        5794 :                 if (props->stroke->color.type == SVG_COLOR_CURRENTCOLOR) {
    1432           0 :                         asp->line_color = GF_COL_ARGB_FIXED(clamped_stroke_opacity, props->color->color.red, props->color->color.green, props->color->color.blue);
    1433        5794 :                 } else if (props->stroke->color.type == SVG_COLOR_RGBCOLOR) {
    1434        5794 :                         asp->line_color = GF_COL_ARGB_FIXED(clamped_stroke_opacity, props->stroke->color.red, props->stroke->color.green, props->stroke->color.blue);
    1435           0 :                 } else if (props->stroke->color.type >= SVG_COLOR_ACTIVE_BORDER) {
    1436           0 :                         asp->line_color = tr_state->visual->compositor->sys_colors[SVG_COLOR_ACTIVE_BORDER - 3];
    1437           0 :                         asp->line_color |= ((u32) (clamped_stroke_opacity*255)) << 24;
    1438             :                 }
    1439             :         }
    1440       25443 :         if (!tr_state->color_mat.identity)
    1441           0 :                 asp->line_color = gf_cmx_apply(&tr_state->color_mat, asp->line_color);
    1442             : 
    1443       25443 :         if (props->stroke_dasharray->type != SVG_STROKEDASHARRAY_NONE) {
    1444         900 :                 asp->pen_props.dash = GF_DASH_STYLE_SVG;
    1445         900 :                 asp->pen_props.dash_offset = props->stroke_dashoffset->value;
    1446         900 :                 asp->pen_props.dash_set = (GF_DashSettings *) &(props->stroke_dasharray->array);
    1447             :         }
    1448       25443 :         asp->line_scale = (props->vector_effect && (*props->vector_effect == SVG_VECTOREFFECT_NONSCALINGSTROKE)) ? 0 : FIX_ONE;
    1449             : 
    1450       25443 :         asp->pen_props.cap = (u8) *props->stroke_linecap;
    1451       25443 :         asp->pen_props.join = (u8) *props->stroke_linejoin;
    1452       25443 :         asp->pen_props.miterLimit = props->stroke_miterlimit->value;
    1453             : 
    1454       25443 :         if (!tr_state->color_mat.identity) {
    1455           0 :                 asp->fill_color = gf_cmx_apply(&tr_state->color_mat, asp->fill_color);
    1456           0 :                 asp->line_color = gf_cmx_apply(&tr_state->color_mat, asp->line_color);
    1457             :         }
    1458       25443 :         return ret;
    1459             : }
    1460             : 
    1461       24872 : static Bool svg_appearance_flag_dirty(u32 flags)
    1462             : {
    1463             : #if 1
    1464             :         /* fill-related */
    1465       24872 :         if (flags & GF_SG_SVG_FILL_DIRTY)                           return 1;
    1466       24647 :         if (flags & GF_SG_SVG_FILLOPACITY_DIRTY)            return 1;
    1467       24556 :         if (flags & GF_SG_SVG_FILLRULE_DIRTY)                       return 1;
    1468             : 
    1469             :         /* stroke-related */
    1470       24552 :         if (flags & GF_SG_SVG_STROKE_DIRTY)                         return 1;
    1471       24552 :         if (flags & GF_SG_SVG_STROKEDASHARRAY_DIRTY)        return 1;
    1472       24552 :         if (flags & GF_SG_SVG_STROKEDASHOFFSET_DIRTY)       return 1;
    1473       24552 :         if (flags & GF_SG_SVG_STROKELINECAP_DIRTY)          return 1;
    1474       24552 :         if (flags & GF_SG_SVG_STROKELINEJOIN_DIRTY)         return 1;
    1475       24552 :         if (flags & GF_SG_SVG_STROKEMITERLIMIT_DIRTY)       return 1;
    1476       24552 :         if (flags & GF_SG_SVG_STROKEOPACITY_DIRTY)          return 1;
    1477       24552 :         if (flags & GF_SG_SVG_STROKEWIDTH_DIRTY)            return 1;
    1478       24552 :         if (flags & GF_SG_SVG_VECTOREFFECT_DIRTY)           return 1;
    1479             : 
    1480             :         /* gradients stops and solidcolor do not affect appearance directly */
    1481       24552 :         return 0;
    1482             : #else
    1483             :         if (flags &
    1484             :                 (GF_SG_SVG_FILL_DIRTY | GF_SG_SVG_FILLOPACITY_DIRTY | GF_SG_SVG_FILLRULE_DIRTY
    1485             :                  | GF_SG_SVG_STROKE_DIRTY | GF_SG_SVG_STROKEDASHARRAY_DIRTY
    1486             :                  | GF_SG_SVG_STROKEDASHOFFSET_DIRTY | GF_SG_SVG_STROKELINECAP_DIRTY
    1487             :                  | GF_SG_SVG_STROKELINEJOIN_DIRTY | GF_SG_SVG_STROKEMITERLIMIT_DIRTY
    1488             :                  | GF_SG_SVG_STROKEOPACITY_DIRTY | GF_SG_SVG_STROKEWIDTH_DIRTY
    1489             :                  | GF_SG_SVG_VECTOREFFECT_DIRTY) )
    1490             :                 return 1;
    1491             :         return 0;
    1492             : #endif
    1493             : }
    1494             : 
    1495       25278 : DrawableContext *drawable_init_context_svg(Drawable *drawable, GF_TraverseState *tr_state)
    1496             : {
    1497             :         DrawableContext *ctx;
    1498             :         assert(tr_state->visual);
    1499             : 
    1500             : #ifndef GPAC_DISABLE_VRML
    1501             :         /*setup SVG based on override appearance node */
    1502       25278 :         if (tr_state->override_appearance) {
    1503         341 :                 return drawable_init_context_mpeg4(drawable, tr_state);
    1504             :         }
    1505             : #endif
    1506             : 
    1507             :         /*switched-off geometry nodes are not drawn*/
    1508       24937 :         if (tr_state->switched_off) return NULL;
    1509             : 
    1510             :         //Get a empty context from the current visual
    1511       24937 :         ctx = visual_2d_get_drawable_context(tr_state->visual);
    1512       24937 :         if (!ctx) return NULL;
    1513             : 
    1514       24937 :         gf_mx2d_copy(ctx->transform, tr_state->transform);
    1515             : 
    1516       24937 :         ctx->drawable = drawable;
    1517             : 
    1518       24937 :         if (tr_state->invalidate_all || svg_appearance_flag_dirty(tr_state->svg_flags)) {
    1519         385 :                 ctx->flags |= CTX_APP_DIRTY;
    1520         385 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("Node %s dirty - invalidating\n", gf_node_get_log_name(drawable->node) ));
    1521             :         }
    1522       24937 :         if (tr_state->svg_flags & (GF_SG_SVG_STROKEDASHARRAY_DIRTY |
    1523             :                                    GF_SG_SVG_STROKEDASHOFFSET_DIRTY |
    1524             :                                    GF_SG_SVG_STROKELINECAP_DIRTY |
    1525             :                                    GF_SG_SVG_STROKELINEJOIN_DIRTY |
    1526             :                                    GF_SG_SVG_STROKEMITERLIMIT_DIRTY |
    1527             :                                    GF_SG_SVG_STROKEWIDTH_DIRTY |
    1528             :                                    GF_SG_SVG_VECTOREFFECT_DIRTY ))
    1529           5 :                 ctx->flags |= CTX_SVG_OUTLINE_GEOMETRY_DIRTY;
    1530             : 
    1531       24937 :         ctx->aspect.fill_texture = NULL;
    1532             : 
    1533             :         /*FIXME - only needed for texture*/
    1534       24937 :         if (!tr_state->color_mat.identity) {
    1535           0 :                 GF_SAFEALLOC(ctx->col_mat, GF_ColorMatrix);
    1536           0 :                 if (ctx->col_mat)
    1537           0 :                         gf_cmx_copy(ctx->col_mat, &tr_state->color_mat);
    1538             :         }
    1539             : 
    1540       24937 :         switch (gf_node_get_tag(ctx->drawable->node) ) {
    1541        2389 :         case TAG_SVG_image:
    1542             :         case TAG_SVG_video:
    1543        2389 :                 ctx->aspect.fill_texture = gf_sc_texture_get_handler(ctx->drawable->node);
    1544        2389 :                 break;
    1545             :         case TAG_SVG_line:
    1546             :         case TAG_SVG_polyline:
    1547             :                 break;
    1548             :         default:
    1549             :                 break;
    1550             :         }
    1551             : 
    1552       24937 :         if (drawable_get_aspect_2d_svg(drawable->node, &ctx->aspect, tr_state))
    1553           0 :                 ctx->flags |= CTX_APP_DIRTY;
    1554             : 
    1555       24937 :         if (ctx->drawable->path) {
    1556       24937 :                 if (*tr_state->svg_props->fill_rule == SVG_FILLRULE_NONZERO) {
    1557       24862 :                         ctx->drawable->path->flags |= GF_PATH_FILL_ZERO_NONZERO;
    1558             :                 } else {
    1559          75 :                         ctx->drawable->path->flags &= ~GF_PATH_FILL_ZERO_NONZERO;
    1560             :                 }
    1561             :         }
    1562             : 
    1563       24937 :         drawable_check_texture_dirty(ctx, drawable, tr_state);
    1564             : 
    1565             :         /*we are drawing on a centered coord surface, remember to flip the texture*/
    1566       24937 :         if (tr_state->fliped_coords)
    1567         158 :                 ctx->flags |= CTX_FLIPED_COORDS;
    1568             : 
    1569             : 
    1570             : #ifdef GF_SR_USE_DEPTH
    1571       24937 :         ctx->depth_gain=tr_state->depth_gain;
    1572       24937 :         ctx->depth_offset=tr_state->depth_offset;
    1573             : #endif
    1574             : 
    1575       24937 :         return ctx;
    1576             : }
    1577             : 
    1578             : 
    1579             : 
    1580             : #endif  //SVG

Generated by: LCOV version 1.13