LCOV - code coverage report
Current view: top level - compositor - visual_manager_2d.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 369 400 92.2 %
Date: 2021-04-29 23:48:07 Functions: 17 18 94.4 %

          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             : 
      27             : 
      28             : #include "visual_manager.h"
      29             : #include "nodes_stacks.h"
      30             : 
      31             : //#define SKIP_CONTEXT
      32             : 
      33     1403938 : Bool gf_irect_overlaps(GF_IRect *rc1, GF_IRect *rc2)
      34             : {
      35     1403938 :         if (! rc2->height || !rc2->width || !rc1->height || !rc1->width) return 0;
      36     1398984 :         if (rc2->x+rc2->width<=rc1->x) return 0;
      37     1078119 :         if (rc2->x>=rc1->x+rc1->width) return 0;
      38      893925 :         if (rc2->y-rc2->height>=rc1->y) return 0;
      39      586076 :         if (rc2->y<=rc1->y-rc1->height) return 0;
      40      440306 :         return 1;
      41             : }
      42             : 
      43             : /*intersects @rc1 with @rc2 - the new @rc1 is the intersection*/
      44     1381985 : void gf_irect_intersect(GF_IRect *rc1, GF_IRect *rc2)
      45             : {
      46     1381985 :         if (! gf_irect_overlaps(rc1, rc2)) {
      47      963072 :                 rc1->width = rc1->height = 0;
      48      963072 :                 return;
      49             :         }
      50      418913 :         if (rc2->x > rc1->x) {
      51       50241 :                 rc1->width -= rc2->x - rc1->x;
      52       50241 :                 rc1->x = rc2->x;
      53             :         }
      54      418913 :         if (rc2->x + rc2->width < rc1->x + rc1->width) {
      55       52838 :                 rc1->width = rc2->width + rc2->x - rc1->x;
      56             :         }
      57      418913 :         if (rc2->y < rc1->y) {
      58       39619 :                 rc1->height -= rc1->y - rc2->y;
      59       39619 :                 rc1->y = rc2->y;
      60             :         }
      61      418913 :         if (rc2->y - rc2->height > rc1->y - rc1->height) {
      62       54009 :                 rc1->height = rc1->y - rc2->y + rc2->height;
      63             :         }
      64             : }
      65             : 
      66             : 
      67           0 : GF_Rect gf_rect_ft(GF_IRect *rc)
      68             : {
      69             :         GF_Rect rcft;
      70       13372 :         rcft.x = INT2FIX(rc->x);
      71       13372 :         rcft.y = INT2FIX(rc->y);
      72       13372 :         rcft.width = INT2FIX(rc->width);
      73       13372 :         rcft.height = INT2FIX(rc->height);
      74           0 :         return rcft;
      75             : }
      76             : 
      77      178666 : DrawableContext *visual_2d_get_drawable_context(GF_VisualManager *visual)
      78             : {
      79             : #ifdef SKIP_CONTEXT
      80             :         return NULL;
      81             : #endif
      82             : 
      83      178666 :         if (!visual->context) {
      84         523 :                 visual->context = NewDrawableContext();
      85         523 :                 visual->cur_context = visual->context;
      86         523 :                 drawctx_reset(visual->context);
      87         523 :                 visual->num_nodes_current_frame ++;
      88         523 :                 return visual->context;
      89             :         }
      90             : //      assert(visual->cur_context);
      91             :         /*current context is OK*/
      92      178143 :         if (!visual->cur_context->drawable) {
      93             :                 /*reset next context in display list for next call*/
      94       59164 :                 if (visual->cur_context->next) visual->cur_context->next->drawable = NULL;
      95       59164 :                 drawctx_reset(visual->cur_context);
      96       59164 :                 return visual->cur_context;
      97             :         }
      98             :         /*need a new context and next one is OK*/
      99      118979 :         if (visual->cur_context->next) {
     100      113960 :                 visual->cur_context = visual->cur_context->next;
     101             : //              assert(visual->cur_context->drawable == NULL);
     102             :                 /*reset next context in display list for next call*/
     103      113960 :                 if (visual->cur_context->next) visual->cur_context->next->drawable = NULL;
     104      113960 :                 drawctx_reset(visual->cur_context);
     105      113960 :                 visual->num_nodes_current_frame ++;
     106      113960 :                 return visual->cur_context;
     107             :         }
     108             :         /*need to create a new context*/
     109        5019 :         visual->cur_context->next = NewDrawableContext();
     110        5019 :         visual->cur_context = visual->cur_context->next;
     111        5019 :         drawctx_reset(visual->cur_context);
     112        5019 :         visual->num_nodes_current_frame ++;
     113             : 
     114             :         //pre-allocate some contexts
     115             : #if 0
     116             :         {
     117             :                 u32 i;
     118             :                 DrawableContext *last = visual->cur_context;
     119             :                 for (i=0; i<50; i++) {
     120             :                         last->next = gf_malloc(sizeof(DrawableContext));
     121             :                         last = last->next;
     122             :                         last->drawable = NULL;
     123             :                         last->col_mat = NULL;
     124             :                 }
     125             :                 last->next = NULL;
     126             :         }
     127             : #endif
     128             : 
     129        5019 :         return visual->cur_context;
     130             : }
     131             : 
     132       36214 : void visual_2d_remove_last_context(GF_VisualManager *visual)
     133             : {
     134             :         assert(visual->cur_context);
     135       36214 :         visual->cur_context->drawable = NULL;
     136       36214 : }
     137             : 
     138             : 
     139        3955 : void visual_2d_drawable_delete(GF_VisualManager *visual, struct _drawable *drawable)
     140             : {
     141             :         DrawableContext *ctx;
     142             :         /*remove drawable from visual list*/
     143        3955 :         struct _drawable_store *it = visual->prev_nodes;
     144             :         struct _drawable_store *prev = NULL;
     145        8470 :         while (it) {
     146         692 :                 if (it->drawable != drawable) {
     147             :                         prev = it;
     148         560 :                         it = prev->next;
     149         560 :                         continue;
     150             :                 }
     151         132 :                 if (prev) prev->next = it->next;
     152          74 :                 else visual->prev_nodes = it->next;
     153         132 :                 if (!it->next) visual->last_prev_entry = prev;
     154         132 :                 gf_free(it);
     155         132 :                 break;
     156             :         }
     157             : 
     158        3955 :         ctx = visual->context;
     159        8644 :         while (ctx && ctx->drawable) {
     160             :                 /*remove visual registration flag*/
     161         734 :                 if (ctx->drawable == drawable) {
     162          83 :                         ctx->flags = 0;
     163          83 :                         ctx->drawable = NULL;
     164             :                 }
     165         734 :                 ctx = ctx->next;
     166             :         }
     167        3955 :         if (drawable->flags & DRAWABLE_IS_OVERLAY) {
     168           0 :                 visual->compositor->video_out->Blit(visual->compositor->video_out, NULL, NULL, NULL, 1);
     169             :         }
     170        3955 : }
     171             : 
     172             : #if 0 //unused
     173             : Bool visual_2d_node_cull(GF_TraverseState *tr_state, GF_Rect *bounds)
     174             : {
     175             :         GF_Rect rc;
     176             :         GF_IRect i_rc;
     177             :         rc = *bounds;
     178             :         gf_mx2d_apply_rect(&tr_state->transform, &rc);
     179             :         i_rc = gf_rect_pixelize(&rc);
     180             :         if (gf_irect_overlaps(&tr_state->visual->top_clipper, &i_rc)) return 1;
     181             :         return 0;
     182             : }
     183             : #endif
     184             : 
     185       24879 : void visual_2d_setup_projection(GF_VisualManager *visual, GF_TraverseState *tr_state)
     186             : {
     187             :         GF_Rect rc;
     188             : 
     189       24879 :         tr_state->visual = visual;
     190             : #ifndef GPAC_DISABLE_VRML
     191       24879 :         tr_state->backgrounds = visual->back_stack;
     192       24879 :         tr_state->viewpoints = visual->view_stack;
     193             : #endif
     194             : 
     195             :         /*setup clipper*/
     196       24879 :         if (visual->center_coords) {
     197       24019 :                 if (!visual->offscreen) {
     198       22205 :                         if (visual->compositor->sz)
     199       22205 :                                 rc = gf_rect_center(INT2FIX(visual->compositor->display_width), INT2FIX(visual->compositor->display_height));
     200             :                         else
     201           0 :                                 rc = gf_rect_center(INT2FIX(visual->compositor->output_width + 2*visual->compositor->vp_x), INT2FIX(visual->compositor->output_height + 2*visual->compositor->vp_y));
     202             :                 } else {
     203        1814 :                         rc = gf_rect_center(INT2FIX(visual->width), INT2FIX(visual->height));
     204             :                 }
     205             :         } else {
     206         860 :                 rc.x = 0;
     207         860 :                 rc.width = INT2FIX(visual->width);
     208         860 :                 rc.y = rc.height = INT2FIX(visual->height);
     209             :         }
     210             :         /*set top-transform to pixelMetrics*/
     211       24879 :         if (!tr_state->pixel_metrics) gf_mx2d_add_scale(&tr_state->transform, tr_state->min_hsize, tr_state->min_hsize);
     212             : 
     213       24879 :         visual->surf_rect = gf_rect_pixelize(&rc);
     214             : 
     215             : //      GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] output rectangle setup - width %d height %d\n", visual->surf_rect.width, visual->surf_rect.height));
     216             : 
     217             :         /*setup top clipper*/
     218       24879 :         if (visual->center_coords) {
     219       24019 :                 rc = gf_rect_center(INT2FIX(visual->width), INT2FIX(visual->height));
     220             :         } else {
     221         860 :                 rc.width = INT2FIX(visual->width);
     222         860 :                 rc.height = INT2FIX(visual->height);
     223         860 :                 rc.x = 0;
     224         860 :                 rc.y = rc.height;
     225         860 :                 if (visual->compositor->visual==visual) {
     226         860 :                         rc.x += INT2FIX(visual->compositor->vp_x);
     227         860 :                         rc.y += INT2FIX(visual->compositor->vp_y);
     228             :                 }
     229             :         }
     230             : 
     231             :         /*setup viewport*/
     232             : #ifndef GPAC_DISABLE_VRML
     233       24879 :         if (gf_list_count(visual->view_stack)) {
     234         364 :                 tr_state->traversing_mode = TRAVERSE_BINDABLE;
     235         364 :                 tr_state->bounds = rc;
     236         364 :                 gf_node_traverse((GF_Node *) gf_list_get(visual->view_stack, 0), tr_state);
     237             :         }
     238             : #endif
     239             : 
     240             : #ifndef GPAC_DISABLE_3D
     241       49758 :         gf_mx_init(tr_state->model_matrix);
     242       24879 :         if (tr_state->camera && (visual->compositor->visual==visual)) {
     243        1128 :                 tr_state->camera->vp.width = INT2FIX(visual->compositor->output_width);
     244        1128 :                 tr_state->camera->vp.height = INT2FIX(visual->compositor->output_height);
     245             :         }
     246             : #endif
     247             : 
     248       24879 :         visual->top_clipper = gf_rect_pixelize(&rc);
     249       24879 :         tr_state->clipper = rc;
     250             : //      GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Cliper setup - %d:%d@%dx%d\n", visual->top_clipper.x, visual->top_clipper.y, visual->top_clipper.width, visual->top_clipper.height));
     251       24879 : }
     252             : 
     253       21466 : GF_Err visual_2d_init_draw(GF_VisualManager *visual, GF_TraverseState *tr_state)
     254             : {
     255             :         GF_Err e;
     256             :         u32 rem, count;
     257             :         struct _drawable_store *it, *prev;
     258             : #ifndef GPAC_DISABLE_VRML
     259             :         DrawableContext *ctx;
     260             :         M_Background2D *bck;
     261             : #endif
     262             :         u32 mode2d;
     263             : 
     264             :         /*reset display list*/
     265       21466 :         visual->cur_context = visual->context;
     266       21466 :         if (visual->context) visual->context->drawable = NULL;
     267       21466 :         visual->has_modif = 0;
     268       21466 :         visual->has_overlays = 0;
     269             : 
     270       21466 :         visual_2d_setup_projection(visual, tr_state);
     271       21466 :         if (!visual->top_clipper.width || !visual->top_clipper.height)
     272             :                 return GF_OK;
     273             : 
     274       21466 :         tr_state->traversing_mode = TRAVERSE_SORT;
     275       21466 :         visual->num_nodes_current_frame = 0;
     276             : 
     277             :         /*setup raster surface, brush and pen */
     278       21466 :         e = visual_2d_init_raster(visual);
     279       21466 :         if (e)
     280             :                 return e;
     281             : 
     282       21466 :         tr_state->immediate_for_defer = GF_FALSE;
     283             :         mode2d = 0;
     284       21466 :         if (tr_state->immediate_draw) {
     285             :                 mode2d = 1;
     286             :         }
     287             :         /*if we're requested to invalidate everything, switch to direct drawing but don't reset bounds*/
     288       21185 :         else if (tr_state->invalidate_all) {
     289         595 :                 tr_state->immediate_draw = 1;
     290         595 :                 tr_state->immediate_for_defer = GF_TRUE;
     291             :                 mode2d = 2;
     292             :         }
     293       21466 :         tr_state->invalidate_all = 0;
     294             : 
     295             :         /*reset prev nodes if any (previous traverse was indirect)*/
     296             :         rem = count = 0;
     297             :         prev = NULL;
     298       21466 :         it = visual->prev_nodes;
     299      146150 :         while (it) {
     300             :                 /*node was not drawn on this visual*/
     301      103218 :                 if (!drawable_flush_bounds(it->drawable, visual, mode2d)) {
     302           6 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Unregistering previously drawn node %s from visual\n", gf_node_get_class_name(it->drawable->node)));
     303             : 
     304             :                         /*remove all bounds info related to this visual and unreg node */
     305           6 :                         drawable_reset_bounds(it->drawable, visual);
     306             : 
     307           6 :                         if (prev) prev->next = it->next;
     308           4 :                         else visual->prev_nodes = it->next;
     309           6 :                         if (!it->next) visual->last_prev_entry = prev;
     310           6 :                         rem++;
     311           6 :                         gf_free(it);
     312           6 :                         it = prev ? prev->next : visual->prev_nodes;
     313             :                 } else {
     314             :                         /*mark drawable as already registered with visual*/
     315      103212 :                         it->drawable->flags |= DRAWABLE_REGISTERED_WITH_VISUAL;
     316             :                         prev = it;
     317      103212 :                         it = it->next;
     318      103212 :                         count++;
     319             :                 }
     320             :         }
     321       21466 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Top visual initialized - %d nodes registered and %d removed - using %s rendering\n", count, rem, mode2d ? "direct" : "dirty-rect"));
     322       21466 :         if (!mode2d) return GF_OK;
     323             : 
     324             : #ifndef GPAC_DISABLE_VRML
     325             :         /*direct mode, draw background*/
     326         876 :         bck = (M_Background2D*) gf_list_get(visual->back_stack, 0);
     327         876 :         if (bck && bck->isBound) {
     328          96 :                 ctx = b2d_get_context(bck, visual->back_stack);
     329          96 :                 if (ctx) {
     330             :                         /*force clearing entire zone, not just viewport, when using color. If texture, we MUST
     331             :                         use the VP clipper in order to compute offsets when blitting bitmaps*/
     332          96 :                         if (ctx->aspect.fill_texture &&ctx->aspect.fill_texture->stream) {
     333          29 :                                 ctx->bi->clip = visual->top_clipper;
     334             :                         } else {
     335          67 :                                 ctx->bi->clip = visual->surf_rect;
     336             :                         }
     337         192 :                         ctx->bi->unclip = gf_rect_ft(&ctx->bi->clip);
     338          96 :                         tr_state->traversing_mode = TRAVERSE_BINDABLE;
     339          96 :                         ctx->flags |= CTX_BACKROUND_NOT_LAYER;
     340          96 :                         gf_node_traverse((GF_Node *) bck, tr_state);
     341          96 :                         tr_state->traversing_mode = TRAVERSE_SORT;
     342          96 :                         ctx->flags &= ~CTX_BACKROUND_NOT_LAYER;
     343             :                 } else {
     344           0 :                         visual->ClearSurface(visual, NULL, 0, 0);
     345             :                 }
     346             :         } else
     347             : #endif
     348             :         {
     349         780 :                 visual->ClearSurface(visual, NULL, 0, 0);
     350             : #ifndef GPAC_DISABLE_3D
     351         780 :                 if (visual->compositor->hybrid_opengl) {
     352         170 :                         visual->ClearSurface(visual, NULL, 0, GF_TRUE);
     353             :                 }
     354             : #endif
     355             :         }
     356             :         return GF_OK;
     357             : }
     358             : 
     359             : 
     360             : 
     361             : /*@rc2 fully contained in @rc1*/
     362         901 : Bool gf_irect_inside(GF_IRect *rc1, GF_IRect *rc2)
     363             : {
     364         901 :         if (!rc1->width || !rc1->height) return 0;
     365         901 :         if ( (rc1->x <= rc2->x)  && (rc1->y >= rc2->y)  && (rc1->x + rc1->width >= rc2->x + rc2->width) && (rc1->y - rc1->height <= rc2->y - rc2->height) )
     366             :                 return 1;
     367         564 :         return 0;
     368             : }
     369             : 
     370             : 
     371             : /*clears list*/
     372             : #define ra_clear(ra) { (ra)->count = 0; }
     373             : 
     374             : /*is list empty*/
     375             : #define ra_is_empty(ra) (!((ra)->count))
     376             : 
     377             : /*adds @rc2 to @rc1 - the new @rc1 contains the old @rc1 and @rc2*/
     378       36420 : void gf_irect_union(GF_IRect *rc1, GF_IRect *rc2)
     379             : {
     380       36420 :         if (!rc1->width || !rc1->height) {
     381           0 :                 *rc1=*rc2;
     382           0 :                 return;
     383             :         }
     384             : 
     385       36420 :         if (rc2->x < rc1->x) {
     386        4425 :                 rc1->width += rc1->x - rc2->x;
     387        4425 :                 rc1->x = rc2->x;
     388             :         }
     389       36420 :         if (rc2->x + rc2->width > rc1->x+rc1->width) rc1->width = rc2->x + rc2->width - rc1->x;
     390       36420 :         if (rc2->y > rc1->y) {
     391        5072 :                 rc1->height += rc2->y - rc1->y;
     392        5072 :                 rc1->y = rc2->y;
     393             :         }
     394       36420 :         if (rc2->y - rc2->height < rc1->y - rc1->height) rc1->height = rc1->y - rc2->y + rc2->height;
     395             : }
     396             : 
     397             : 
     398             : /*adds rectangle to the list performing union test*/
     399       22409 : void ra_union_rect(GF_RectArray *ra, GF_IRect *rc)
     400             : {
     401             :         u32 i;
     402             : 
     403             :         assert(rc->width && rc->height);
     404             : 
     405       22969 :         for (i=0; i<ra->count; i++) {
     406       21953 :                 if (gf_irect_overlaps(&ra->list[i].rect, rc)) {
     407       21393 :                         gf_irect_union(&ra->list[i].rect, rc);
     408       21393 :                         return;
     409             :                 }
     410             :         }
     411        1016 :         ra_add(ra, rc);
     412             : }
     413             : 
     414             : /*returns relation between rc1 and rc2:
     415             :  0: rectangles are disjoint
     416             :  1: rectangles overlap
     417             :  2: rc2 completely covers rc1
     418             : 
     419             : */
     420       83652 : static u32 gf_irect_relation(GF_IRect *rc1, GF_IRect *rc2)
     421             : {
     422       83652 :         if (! rc2->height || !rc2->width || !rc1->height || !rc1->width) return 0;
     423       83652 :         if (rc2->x+rc2->width<=rc1->x) return 0;
     424       63120 :         if (rc2->x>=rc1->x+rc1->width) return 0;
     425       41141 :         if (rc2->y-rc2->height>=rc1->y) return 0;
     426       33476 :         if (rc2->y<=rc1->y-rc1->height) return 0;
     427             : 
     428       27657 :         if ( (rc2->x <= rc1->x)  && (rc2->y >= rc1->y)  && (rc2->x + rc2->width >= rc1->x + rc1->width) && (rc2->y - rc2->height <= rc1->y - rc1->height) )
     429             :                 return 2;
     430             : 
     431       15027 :         return 1;
     432             : }
     433             : 
     434             : /*refreshes the content of the array to have only non-overlapping rects*/
     435       19725 : void ra_refresh(GF_RectArray *ra)
     436             : {
     437             :         u32 i, j, k;
     438       44487 : restart:
     439       82041 :         for (i=0; i<ra->count; i++) {
     440       80040 :                 for (j=i+1; j<ra->count; j++) {
     441       62211 :                         switch (gf_irect_relation(&ra->list[j].rect, &ra->list[i].rect)) {
     442             : 
     443             :                         /*both rectangles overlap, merge them and remove opaque node info*/
     444        6330 :                         case 1:
     445        6330 :                                 gf_irect_union(&ra->list[i].rect, &ra->list[j].rect);
     446             : #ifdef TRACK_OPAQUE_REGIONS
     447             :                                 /*current dirty rect is no longer opaque*/
     448             :                                 ra->list[i].opaque_node_index = 0;
     449             : #endif
     450             :                         /*FALLTHROUGH*/
     451             :                         /*first rectangle covers second, just remove*/
     452       18325 :                         case 2:
     453             :                                 /*remove rect*/
     454       18325 :                                 k = ra->count - j - 1;
     455       18325 :                                 if (k) {
     456        9559 :                                         memmove(&ra->list[j], & ra->list[j+1], sizeof(GF_IRect)*k);
     457             :                                 }
     458       18325 :                                 ra->count--;
     459       18325 :                                 if (ra->count>=2)
     460             :                                         goto restart;
     461             :                                 return;
     462             :                         default:
     463             :                                 break;
     464             :                         }
     465             :                 }
     466             :         }
     467             : }
     468             : 
     469      126079 : static u32 register_context_rect(GF_RectArray *ra, DrawableContext *ctx, u32 ctx_idx, DrawableContext **first_opaque)
     470             : {
     471             :         u32 i;
     472             :         Bool needs_redraw;
     473             : #ifdef TRACK_OPAQUE_REGIONS
     474             :         Bool is_transparent = 1;
     475             : #endif
     476      126079 :         GF_IRect *rc = &ctx->bi->clip;
     477             :         assert(rc->width && rc->height);
     478             : 
     479             :         /*node is modified*/
     480      126079 :         needs_redraw = (ctx->flags & CTX_REDRAW_MASK) ? 1 : 0;
     481             : 
     482             :         /*node is not transparent*/
     483      126079 :         if ((ctx->flags & CTX_NO_ANTIALIAS) && !(ctx->flags & CTX_IS_TRANSPARENT) ) {
     484             : #ifdef TRACK_OPAQUE_REGIONS
     485             :                 is_transparent = 0;
     486             : #endif
     487       20175 :                 if ((*first_opaque==NULL) && needs_redraw) *first_opaque = ctx;
     488             :         }
     489             : 
     490             : #ifndef GPAC_DISABLE_3D
     491      126079 :         if (ctx->flags & CTX_HYBOGL_NO_CLEAR) {
     492             :                 return 2;
     493             :         }
     494             : #endif
     495             : 
     496       84534 :         for (i=0; i<ra->count; i++) {
     497       93866 :                 if (needs_redraw) {
     498       21441 :                         switch (gf_irect_relation(&ra->list[i].rect, rc)) {
     499             :                         /*context intersects an existing rectangle, merge them and remove opaque idx info*/
     500        8697 :                         case 1:
     501        8697 :                                 gf_irect_union(&ra->list[i].rect, rc);
     502             : #ifdef TRACK_OPAQUE_REGIONS
     503             :                                 ra->list[i].opaque_node_index = 0;
     504             : #endif
     505             :                                 return 1;
     506             :                         /*context covers an existing rectangle, replace rect and add opaque idx info*/
     507         635 :                         case 2:
     508         635 :                                 ra->list[i].rect= *rc;
     509             : #ifdef TRACK_OPAQUE_REGIONS
     510             :                                 ra->list[i].opaque_node_index = is_transparent ? 0 : ctx_idx;
     511             : #endif
     512             :                                 return 1;
     513             :                         }
     514             :                 }
     515             : #ifdef TRACK_OPAQUE_REGIONS
     516             :                 /*context unchanged coverring an entire area*/
     517             :                 else if (!is_transparent && gf_irect_inside(rc, &ra->list[i].rect)) {
     518             :                         /*remove rect*/
     519             :                         u32 k = ra->count - i - 1;
     520             :                         if (k) {
     521             :                                 memmove(&ra->list[i], & ra->list[i+1], sizeof(GF_RectArrayEntry)*k);
     522             :                         }
     523             :                         ra->count--;
     524             :                         i--;
     525             :                 }
     526             : #endif
     527             :         }
     528             :         /*not found, add rect*/
     529      116545 :         if (needs_redraw) {
     530       19158 :                 ra_add(ra, rc);
     531             : #ifdef TRACK_OPAQUE_REGIONS
     532             :                 ra->list[ra->count-1].opaque_node_index = is_transparent ? 0 : ctx_idx;
     533             : #endif
     534             :         }
     535             :         return 1;
     536             : }
     537             : 
     538             : 
     539       18238 : static void register_dirty_rect(GF_RectArray *ra, GF_IRect *rc)
     540             : {
     541       18238 :         if (!rc->width || !rc->height) return;
     542             : 
     543             :         /*technically this is correct however the gain is not that big*/
     544             : #if 0
     545             : 
     546             : #ifdef TRACK_OPAQUE_REGIONS
     547             :         u32 i;
     548             :         for (i=0; i<ra->count; i++) {
     549             :                 switch (gf_irect_relation(rc, &ra->list[i].rect)) {
     550             :                 /*dirty area intersects this dirty rectangle, merge them and remove opaque idx info*/
     551             :                 case 1:
     552             :                         gf_irect_union(&ra->list[i].rect, rc);
     553             :                         ra->list[i].opaque_node_index = 0;
     554             :                         return;
     555             :                 /*dirty area is covered by this dirty rectangle, nothing to do*/
     556             :                 case 2:
     557             :                         return;
     558             :                 }
     559             :         }
     560             : #endif
     561             :         /*not found, add rect*/
     562             :         ra_add(ra, rc);
     563             : #ifdef TRACK_OPAQUE_REGIONS
     564             :         ra->list[ra->count-1].opaque_node_index = 0;
     565             : #endif
     566             : 
     567             : #else
     568             : 
     569       18238 :         ra_add(ra, rc);
     570             : 
     571             : #ifdef TRACK_OPAQUE_REGIONS
     572             :         ra->list[ra->count-1].opaque_node_index = 0;
     573             : #endif
     574             : 
     575             : #endif
     576             : }
     577             : 
     578             : 
     579       21466 : Bool visual_2d_terminate_draw(GF_VisualManager *visual, GF_TraverseState *tr_state)
     580             : {
     581             :         u32 k, i, count, num_nodes, num_changed;
     582             :         GF_IRect refreshRect;
     583             :         Bool redraw_all;
     584             :         Bool hyb_force_redraw=GF_FALSE;
     585             :         u32 hyb_force_background = 0;
     586             : #ifndef GPAC_DISABLE_VRML
     587             :         M_Background2D *bck = NULL;
     588             :         DrawableContext *bck_ctx = NULL;
     589             : #endif
     590             :         DrawableContext *ctx;
     591             :         struct _drawable_store *it, *prev;
     592       21466 :         DrawableContext *first_opaque = NULL;
     593             :         Bool has_clear = 0;
     594             :         Bool has_changed = 0;
     595             :         Bool redraw_all_on_background_change = GF_TRUE;
     596             : 
     597             :         /*in direct mode the visual is always redrawn*/
     598       21466 :         if (tr_state->immediate_draw) {
     599             :                 /*flush pending contexts due to overlays*/
     600         876 :                 visual_2d_flush_overlay_areas(visual, tr_state);
     601             : 
     602         876 :                 visual_2d_release_raster(visual);
     603         876 :                 visual_clean_contexts(visual);
     604         876 :                 visual->num_nodes_prev_frame = visual->num_nodes_current_frame;
     605         876 :                 return 1;
     606             :         }
     607             : 
     608             :         num_changed = 0;
     609             : 
     610             :         /*if the aspect ratio has changed redraw everything*/
     611       20590 :         redraw_all = tr_state->invalidate_all;
     612             : 
     613             : #ifndef GPAC_DISABLE_3D
     614       20590 :         if (visual->compositor->hybrid_opengl && !visual->offscreen) redraw_all_on_background_change = GF_FALSE;
     615             : #endif
     616             :         /*check for background changes for transparent nodes*/
     617             : #ifndef GPAC_DISABLE_VRML
     618       20590 :         bck = (M_Background2D*)gf_list_get(visual->back_stack, 0);
     619       20590 :         if (bck) {
     620       17907 :                 if (!bck->isBound) {
     621           0 :                         if (visual->last_had_back) {
     622           0 :                                 if (redraw_all_on_background_change) redraw_all = 1;
     623             :                                 else hyb_force_background = 1;
     624             :                         }
     625           0 :                         visual->last_had_back = 0;
     626             :                 } else {
     627       17907 :                         bck_ctx = b2d_get_context(bck, visual->back_stack);
     628       17907 :                         if (!visual->last_had_back || (bck_ctx->flags & CTX_REDRAW_MASK) ) {
     629        1019 :                                 if (redraw_all_on_background_change) redraw_all = 1;
     630             :                         }
     631             :                         /*in hybridGL we will have to force background draw even if no change, since backbuffer GL is not persistent*/
     632       17907 :                         if (!redraw_all_on_background_change)
     633             :                                 hyb_force_background = 1;
     634             : 
     635       17907 :                         visual->last_had_back = (bck_ctx->aspect.fill_texture && !bck_ctx->aspect.fill_texture->transparent) ? 2 : 1;
     636             :                 }
     637             :         } else
     638             : #endif
     639        2683 :                 if (visual->last_had_back) {
     640           0 :                         visual->last_had_back = 0;
     641           0 :                         if (redraw_all_on_background_change) redraw_all = 1;
     642             :                         else hyb_force_background = 1;
     643        2683 :                 } else if (!redraw_all_on_background_change) {
     644             :                         hyb_force_background = 1;
     645             :                 }
     646             : 
     647             :         num_nodes = 0;
     648       20590 :         ctx = visual->context;
     649      172104 :         while (ctx && ctx->drawable) {
     650      130924 :                 num_nodes++;
     651             : 
     652      130924 :                 drawctx_update_info(ctx, visual);
     653      130924 :                 if (!redraw_all) {
     654             :                         u32 res;
     655             : //                      assert( gf_irect_inside(&visual->top_clipper, &ctx->bi->clip) );
     656      126079 :                         res = register_context_rect(&visual->to_redraw, ctx, num_nodes, &first_opaque);
     657      126079 :                         if (res) {
     658      126079 :                                 num_changed ++;
     659      126079 :                                 if (res==2)
     660             :                                         hyb_force_redraw=GF_TRUE;
     661             :                         }
     662             : 
     663             :                 }
     664      130924 :                 ctx = ctx->next;
     665             :         }
     666             : 
     667             :         /*garbage collection*/
     668             : 
     669             :         /*clear all remaining bounds since last frames (the ones that moved or that are not drawn this frame)*/
     670             :         prev = NULL;
     671       20590 :         it = visual->prev_nodes;
     672      146157 :         while (it) {
     673      123456 :                 while (drawable_get_previous_bound(it->drawable, &refreshRect, visual)) {
     674       18479 :                         if (!redraw_all) {
     675             :                                 //assert( gf_irect_inside(&visual->top_clipper, &refreshRect) );
     676       18238 :                                 gf_irect_intersect(&refreshRect, &visual->top_clipper);
     677       18238 :                                 register_dirty_rect(&visual->to_redraw, &refreshRect);
     678             :                                 has_clear=1;
     679             :                         }
     680             :                 }
     681             :                 /*if node is marked as undrawn, remove from visual*/
     682      104977 :                 if (!(it->drawable->flags & DRAWABLE_DRAWN_ON_VISUAL)) {
     683        1767 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Node %s no longer on visual - unregistering it\n", gf_node_get_class_name(it->drawable->node)));
     684             : 
     685             :                         /*remove all bounds info related to this visual and unreg node */
     686        1767 :                         drawable_reset_bounds(it->drawable, visual);
     687             : 
     688        1767 :                         it->drawable->flags &= ~DRAWABLE_REGISTERED_WITH_VISUAL;
     689             : 
     690        1767 :                         if (it->drawable->flags & DRAWABLE_IS_OVERLAY) {
     691           0 :                                 visual->compositor->video_out->Blit(visual->compositor->video_out, NULL, NULL, NULL, 1);
     692             :                         }
     693             : 
     694        1767 :                         if (prev) prev->next = it->next;
     695          70 :                         else visual->prev_nodes = it->next;
     696        1767 :                         if (!it->next) visual->last_prev_entry = prev;
     697        1767 :                         gf_free(it);
     698        1767 :                         it = prev ? prev->next : visual->prev_nodes;
     699             :                 } else {
     700             :                         prev = it;
     701      103210 :                         it = it->next;
     702             :                 }
     703             :         }
     704             : 
     705       20590 :         if (redraw_all) {
     706         865 :                 ra_clear(&visual->to_redraw);
     707         865 :                 ra_add(&visual->to_redraw, &visual->surf_rect);
     708             : #ifdef TRACK_OPAQUE_REGIONS
     709             :                 visual->to_redraw.list[0].opaque_node_index=0;
     710             : #endif
     711             :         } else {
     712       19725 :                 ra_refresh(&visual->to_redraw);
     713             : 
     714       19725 :                 if (visual->compositor->debug_defer) {
     715         248 :                         visual->ClearSurface(visual, &visual->top_clipper, 0, 0);
     716             :                 }
     717             :         }
     718             : 
     719             :         /*nothing to redraw*/
     720       20590 :         if (ra_is_empty(&visual->to_redraw) ) {
     721        5655 :                 if (!hyb_force_redraw && !hyb_force_background) {
     722             : #ifndef GPAC_DISABLE_3D
     723             :                         //force canvas draw
     724        5611 :                         visual->nb_objects_on_canvas_since_last_ogl_flush = 1;
     725             : #endif
     726        5611 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] No changes found since last frame - skipping redraw\n"));
     727             :                         goto exit;
     728             :                 }
     729             : 
     730             :                 //openGL, force redraw of complete scene but signal we shoud only draw the background, not clear the canvas (nothing to redraw except GL textures)
     731          44 :                 if (hyb_force_redraw) { 
     732             :                         hyb_force_background = 2;
     733           0 :                         ra_add(&visual->to_redraw, &visual->surf_rect);
     734             :                 }
     735             :         }
     736             :         has_changed = 1;
     737       14979 :         tr_state->traversing_mode = TRAVERSE_DRAW_2D;
     738             : 
     739             :         //if only one opaque object has changed and not moved, skip background unless hybgl mode
     740       14979 :         if (!visual->compositor->hybrid_opengl && !hyb_force_redraw && !hyb_force_background && first_opaque && (visual->to_redraw.count==1) && irect_rect_equal(&first_opaque->bi->clip, &visual->to_redraw.list[0].rect)) {
     741           0 :                 visual->has_modif=0;
     742           0 :                 goto skip_background;
     743             :         }
     744             : 
     745             :         /*redraw everything*/
     746             : #ifndef GPAC_DISABLE_VRML
     747       14979 :         if (bck_ctx) {
     748       13276 :                 drawable_check_bounds(bck_ctx, visual);
     749       13276 :                 tr_state->ctx = bck_ctx;
     750       13276 :                 tr_state->appear = NULL;
     751       13276 :                 visual->draw_node_index = 0;
     752             : 
     753             :                 /*force clearing entire zone, not just viewport, when using color. If texture, we MUST
     754             :                 use the VP clipper in order to compute offsets when blitting bitmaps*/
     755       13276 :                 if (bck_ctx->aspect.fill_texture && bck_ctx->aspect.fill_texture->stream) {
     756         839 :                         bck_ctx->bi->clip = visual->top_clipper;
     757             :                 } else {
     758       12437 :                         bck_ctx->bi->clip = visual->surf_rect;
     759             :                 }
     760       26552 :                 bck_ctx->bi->unclip = gf_rect_ft(&bck_ctx->bi->clip);
     761       13276 :                 bck_ctx->next = visual->context;
     762       13276 :                 bck_ctx->flags |= CTX_BACKROUND_NOT_LAYER;
     763             : 
     764             :                 //for hybrid openGL, only redraw background but do not erase canvas
     765       13276 :                 if (hyb_force_background==2)
     766           0 :                         bck_ctx->flags |= CTX_BACKROUND_NO_CLEAR;
     767             : 
     768       13276 :                 gf_node_traverse(bck_ctx->drawable->node, tr_state);
     769             : 
     770       13276 :                 bck_ctx->flags &= ~CTX_BACKROUND_NOT_LAYER;
     771       13276 :                 bck_ctx->flags &= ~CTX_BACKROUND_NO_CLEAR;
     772             :         } else
     773             : #endif /*GPAC_DISABLE_VRML*/
     774             :         {
     775             : 
     776             : #ifndef GPAC_DISABLE_3D
     777             :                 //cleanup openGL screen
     778        1703 :                 if (visual->compositor->hybrid_opengl) {
     779         351 :                         compositor_2d_hybgl_clear_surface(tr_state->visual, NULL, 0, GF_FALSE);
     780             :                 }
     781             : #endif
     782             : 
     783             :                 //and clean dirty rect - for hybrid openGL this will clear the canvas, otherwise the 2D backbuffer
     784        1703 :                 count = visual->to_redraw.count;
     785        3597 :                 for (k=0; k<count; k++) {
     786             :                         GF_IRect rc;
     787             :                         /*opaque area, skip*/
     788             : #ifdef TRACK_OPAQUE_REGIONS
     789             :                         if (visual->to_redraw.list[k].opaque_node_index > 0) continue;
     790             : #endif
     791        1894 :                         rc = visual->to_redraw.list[k].rect;
     792        1894 :                         visual->ClearSurface(visual, &rc, 0, 1);
     793             :                 }
     794             :         }
     795       14979 :         if (!visual->to_redraw.count) {
     796          44 :                 visual->has_modif=0;
     797             : #ifndef GPAC_DISABLE_3D
     798             :                 //force canvas flush
     799          44 :                 visual->nb_objects_on_canvas_since_last_ogl_flush = 1;
     800             : #endif
     801          44 :                 goto exit;
     802             :         }
     803             : 
     804       20457 :         if (!redraw_all && !has_clear) visual->has_modif=0;
     805             : 
     806       24348 : skip_background:
     807             : 
     808             : #ifndef GPAC_DISABLE_LOG
     809       14935 :         if (gf_log_tool_level_on(GF_LOG_COMPOSE, GF_LOG_DEBUG)) {
     810           0 :                 GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redraw %d / %d nodes (all: %s - %d dirty rects\n)", num_changed, num_nodes, redraw_all ? "yes" : "no", visual->to_redraw.count));
     811           0 :                 if (visual->to_redraw.count>1) GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("\n"));
     812             : 
     813           0 :                 for (i=0; i<visual->to_redraw.count; i++) {
     814           0 :                         GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("\tDirtyRect #%d: %d:%d@%dx%d\n", i+1, visual->to_redraw.list[i].rect.x, visual->to_redraw.list[i].rect.y, visual->to_redraw.list[i].rect.width, visual->to_redraw.list[i].rect.height));
     815             :                         assert(visual->to_redraw.list[i].rect.width);
     816             :                 }
     817             :         }
     818             : #endif
     819             : 
     820       14935 :         visual->draw_node_index = 0;
     821             : 
     822       14935 :         ctx = visual->context;
     823      142357 :         while (ctx && ctx->drawable) {
     824             : 
     825      112487 :                 visual->draw_node_index ++;
     826      112487 :                 tr_state->ctx = ctx;
     827             : 
     828             :                 /*if overlay we cannot remove the context and cannot draw directly*/
     829      112487 :                 if (! visual_2d_overlaps_overlay(tr_state->visual, ctx, tr_state)) {
     830             : 
     831      112487 :                         if (ctx->drawable->flags & DRAWABLE_USE_TRAVERSE_DRAW) {
     832       66286 :                                 gf_node_traverse(ctx->drawable->node, tr_state);
     833             :                         } else {
     834       46201 :                                 drawable_draw(ctx->drawable, tr_state);
     835             :                         }
     836             :                 }
     837      112487 :                 ctx = ctx->next;
     838             :         }
     839             :         /*flush pending contexts due to overlays*/
     840       14935 :         visual_2d_flush_overlay_areas(visual, tr_state);
     841             : #ifndef GPAC_DISABLE_VRML
     842       14935 :         if (bck_ctx) bck_ctx->next = NULL;
     843             : #endif
     844             : 
     845       14935 :         if (visual->direct_flush) {
     846             :                 GF_DirtyRectangles dr;
     847           0 :                 dr.count = visual->to_redraw.count;
     848           0 :                 dr.list = gf_malloc(sizeof(GF_IRect)*dr.count);
     849           0 :                 for (i=0; i<dr.count; i++) {
     850           0 :                         dr.list[i] = visual->to_redraw.list[i].rect;
     851             :                 }
     852           0 :                 visual->compositor->video_out->FlushRectangles(visual->compositor->video_out, &dr);
     853           0 :                 visual->compositor->skip_flush = 1;
     854           0 :                 gf_free(dr.list);
     855             :         }
     856             : 
     857       35525 : exit:
     858             :         /*clear dirty rects*/
     859       20590 :         ra_clear(&visual->to_redraw);
     860       20590 :         visual_2d_release_raster(visual);
     861       20590 :         visual_clean_contexts(visual);
     862       20590 :         visual->num_nodes_prev_frame = visual->num_nodes_current_frame;
     863       20590 :         return has_changed;
     864             : }
     865             : 
     866       21466 : Bool visual_2d_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseState *tr_state, Bool is_root_visual)
     867             : {
     868             :         GF_Matrix2D backup;
     869             :         u32 i;
     870             :         Bool res;
     871             :         GF_Err e;
     872             : #ifndef GPAC_DISABLE_LOG
     873       21466 :         u32 itime, time = gf_sys_clock();
     874             : #endif
     875             : 
     876       21466 :         gf_mx2d_copy(backup, tr_state->transform);
     877       21466 :         visual->bounds_tracker_modif_flag = DRAWABLE_HAS_CHANGED;
     878             : 
     879             : #ifndef GPAC_DISABLE_3D
     880       21466 :         if (visual->compositor->hybrid_opengl && visual->compositor->fbo_id) {
     881         876 :                 compositor_3d_enable_fbo(visual->compositor, GF_TRUE);
     882             :         }
     883             : #endif
     884             : 
     885       21466 :         e = visual_2d_init_draw(visual, tr_state);
     886       21466 :         if (e) {
     887             :                 gf_mx2d_copy(tr_state->transform, backup);
     888           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Visual2D] Cannot init draw phase: %s\n", gf_error_to_string(e)));
     889             : #ifndef GPAC_DISABLE_3D
     890           0 :                 if (visual->compositor->hybrid_opengl && visual->compositor->fbo_id) {
     891           0 :                         compositor_3d_enable_fbo(visual->compositor, GF_FALSE);
     892             :                 }
     893             : #endif
     894             :                 return 0;
     895             :         }
     896             : 
     897             : #ifndef GPAC_DISABLE_LOG
     898       21466 :         itime = gf_sys_clock();
     899       21466 :         visual->compositor->traverse_setup_time = itime - time;
     900             :         time = itime;
     901             : #endif
     902             : 
     903       21466 :         GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Traversing scene subtree (root node %s)\n", root ? gf_node_get_class_name(root) : "none"));
     904             : 
     905       21466 :         if (is_root_visual) {
     906             :                 GF_SceneGraph *sg;
     907       19702 :                 gf_node_traverse(root, tr_state);
     908             : 
     909       19702 :                 i=0;
     910       39519 :                 while ((sg = (GF_SceneGraph*)gf_list_enum(visual->compositor->extra_scenes, &i))) {
     911         115 :                         gf_sc_traverse_subscene(visual->compositor, root, sg, tr_state);
     912             :                 }
     913             :         } else {
     914        1764 :                 gf_node_traverse(root, tr_state);
     915             :         }
     916             : 
     917             : #ifndef GPAC_DISABLE_LOG
     918       21466 :         itime = gf_sys_clock();
     919       21466 :         visual->compositor->traverse_and_direct_draw_time = itime - time;
     920             :         time = itime;
     921             : #endif
     922             : 
     923             :         gf_mx2d_copy(tr_state->transform, backup);
     924       21466 :         res = visual_2d_terminate_draw(visual, tr_state);
     925             : 
     926             : #ifndef GPAC_DISABLE_LOG
     927       21466 :         if (!tr_state->immediate_draw) {
     928       20590 :                 visual->compositor->indirect_draw_time = gf_sys_clock() - time;
     929             :         }
     930             : #endif
     931             : 
     932             : #ifndef GPAC_DISABLE_3D
     933       21466 :         if (visual->compositor->hybrid_opengl && visual->compositor->fbo_id) {
     934         876 :                 compositor_3d_enable_fbo(visual->compositor, GF_FALSE);
     935             :         }
     936             : #endif
     937             :         return res;
     938             : }
     939             : 
     940             : 
     941        3413 : void visual_2d_pick_node(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children)
     942             : {
     943             :         GF_Matrix2D backup;
     944        3413 :         visual->bounds_tracker_modif_flag = DRAWABLE_HAS_CHANGED_IN_LAST_TRAVERSE;
     945             : 
     946        3413 :         gf_mx2d_copy(backup, tr_state->transform);
     947             : 
     948        3413 :         visual_2d_setup_projection(visual, tr_state);
     949             : 
     950        3413 :         visual->compositor->hit_node = NULL;
     951        3413 :         tr_state->ray.orig.x = INT2FIX(ev->mouse.x);
     952        3413 :         tr_state->ray.orig.y = INT2FIX(ev->mouse.y);
     953        3413 :         tr_state->ray.orig.z = 0;
     954        3413 :         tr_state->ray.dir.x = 0;
     955        3413 :         tr_state->ray.dir.y = 0;
     956        3413 :         tr_state->ray.dir.z = -FIX_ONE;
     957             : 
     958        3413 :         visual->compositor->hit_world_point = tr_state->ray.orig;
     959        3413 :         visual->compositor->hit_world_ray = tr_state->ray;
     960        3413 :         visual->compositor->hit_square_dist = 0;
     961             : 
     962        3413 :         gf_list_reset(visual->compositor->sensors);
     963        3413 :         tr_state->traversing_mode = TRAVERSE_PICK;
     964             : 
     965             :         /*not the root scene, use children list*/
     966        3413 :         if (visual->compositor->visual != visual) {
     967         228 :                 while (children) {
     968         178 :                         gf_node_traverse(children->node, tr_state);
     969         178 :                         children = children->next;
     970             :                 }
     971             :         } else {
     972        3363 :                 u32 i = 0;
     973        3363 :                 GF_SceneGraph *sg = visual->compositor->scene;
     974        3363 :                 GF_Node *root = gf_sg_get_root_node(sg);
     975        3363 :                 gf_node_traverse(root, tr_state);
     976        6756 :                 while ((sg = (GF_SceneGraph*)gf_list_enum(visual->compositor->extra_scenes, &i))) {
     977          30 :                         gf_sc_traverse_subscene(visual->compositor, root, sg, tr_state);
     978             :                 }
     979             :         }
     980             :         gf_mx2d_copy(tr_state->transform, backup);
     981        3413 : }
     982             : 

Generated by: LCOV version 1.13