Bool visual_2d_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseState *tr_state, Bool is_root_visual) { GF_SceneGraph *sg; GF_Matrix2D backup; u32 i; Bool res; GF_Err e; #ifndef GPAC_DISABLE_LOG u32 itime, time = gf_sys_clock(); #endif gf_mx2d_copy(backup, tr_state->transform); visual->bounds_tracker_modif_flag = DRAWABLE_HAS_CHANGED; e = visual_2d_init_draw(visual, tr_state); if (e) { gf_mx2d_copy(tr_state->transform, backup); GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Visual2D] Cannot init draw phase: %s\n", gf_error_to_string(e))); return 0; } #ifndef GPAC_DISABLE_LOG itime = gf_sys_clock(); visual->compositor->traverse_setup_time = itime - time; time = itime; #endif GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Traversing scene subtree (root node %s)\n", root ? gf_node_get_class_name(root) : "none")); if (is_root_visual) { gf_node_traverse(root, tr_state); i=0; while ((sg = (GF_SceneGraph*)gf_list_enum(visual->compositor->extra_scenes, &i))) { gf_sc_traverse_subscene(visual->compositor, root, sg, tr_state); } } else { gf_node_traverse(root, tr_state); } #ifndef GPAC_DISABLE_LOG itime = gf_sys_clock(); visual->compositor->traverse_and_direct_draw_time = itime - time; time = itime; #endif gf_mx2d_copy(tr_state->transform, backup); res = visual_2d_terminate_draw(visual, tr_state); #ifndef GPAC_DISABLE_LOG if (!tr_state->immediate_draw) { visual->compositor->indirect_draw_time = gf_sys_clock() - time; } #endif return res; }
void TraverseCollision(GF_Node *node, void *rs, Bool is_destroy) { u32 collide_flags; SFVec3f last_point; Fixed last_dist; M_Collision *col = (M_Collision *)node; GF_TraverseState *tr_state = (GF_TraverseState *)rs; GroupingNode *group = (GroupingNode *) gf_node_get_private(node); if (is_destroy) { gf_sc_check_focus_upon_destroy(node); group_3d_delete(node); return; } if (tr_state->traversing_mode != TRAVERSE_COLLIDE) { group_3d_traverse(node, group, tr_state); } else if (col->collide) { collide_flags = tr_state->camera->collide_flags; last_dist = tr_state->camera->collide_dist; tr_state->camera->collide_flags &= 0; tr_state->camera->collide_dist = FIX_MAX; last_point = tr_state->camera->collide_point; if (col->proxy) { /*always check bounds to update any dirty node*/ tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; gf_node_traverse(col->proxy, rs); tr_state->traversing_mode = TRAVERSE_COLLIDE; gf_node_traverse(col->proxy, rs); } else { group_3d_traverse(node, group, (GF_TraverseState*)rs); } if (tr_state->camera->collide_flags & CF_COLLISION) { col->collideTime = gf_node_get_scene_time(node); gf_node_event_out(node, 5/*"collideTime"*/); /*if not closer restore*/ if (collide_flags && (last_dist<tr_state->camera->collide_dist)) { tr_state->camera->collide_flags = collide_flags; tr_state->camera->collide_dist = last_dist; tr_state->camera->collide_point = last_point; } } else { tr_state->camera->collide_flags = collide_flags; tr_state->camera->collide_dist = last_dist; } } }
/*sound2D wraper - spacialization is not supported yet*/ static void TraverseSound2D(GF_Node *node, void *rs, Bool is_destroy) { GF_TraverseState *tr_state = (GF_TraverseState*) rs; M_Sound2D *snd = (M_Sound2D *)node; Sound2DStack *st = (Sound2DStack *)gf_node_get_private(node); if (is_destroy) { gf_free(st); return; } if (!snd->source) return; /*this implies no DEF/USE for real location...*/ st->pos.x = snd->location.x; st->pos.y = snd->location.y; st->pos.z = 0; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) gf_mx_apply_vec(&tr_state->model_matrix, &st->pos); else #endif gf_mx2d_apply_coords(&tr_state->transform, &st->pos.x, &st->pos.y); tr_state->sound_holder = &st->snd_ifce; gf_node_traverse((GF_Node *) snd->source, tr_state); tr_state->sound_holder = NULL; /*never cull Sound2d*/ tr_state->disable_cull = 1; }
/* This is the generic routine for children traversing */ void compositor_svg_traverse_children(GF_ChildNodeItem *children, GF_TraverseState *tr_state) { while (children) { gf_node_traverse(children->node, tr_state); children = children->next; } }
static void svg_traverse_text_block(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state, GF_List *spans) { GF_ChildNodeItem *child; Bool is_switch = GF_FALSE; switch (gf_node_get_tag(node)) { case TAG_DOMText: svg_traverse_domtext(node, atts, tr_state, spans, NULL); break; case TAG_SVG_tspan: /*mark tspan as dirty to force rebuild*/ gf_node_dirty_set(node, 0, GF_FALSE); gf_node_traverse(node, tr_state); break; case TAG_SVG_switch: is_switch = GF_TRUE; case TAG_SVG_a: child = ((GF_ParentNode *)node)->children; while (child) { if (is_switch) { SVGAllAttributes a_atts; gf_svg_flatten_attributes((SVG_Element*)child->node, &a_atts); if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &a_atts)) { svg_traverse_text_block(child->node, atts, tr_state, spans); break; } } else if (gf_node_get_tag(child->node)==TAG_DOMText) { svg_traverse_domtext(child->node, atts, tr_state, spans, node); } child = child->next; } break; default: break; } }
void visual_2d_texture_path_opengl_auto(GF_VisualManager *visual, GF_Path *path, GF_TextureHandler *txh, struct _drawable_context *ctx, GF_Rect *orig_bounds, GF_Matrix2D *ext_mx, GF_TraverseState *tr_state) { GF_Rect clipper; u32 prev_mode = tr_state->traversing_mode; u32 prev_type_3d = tr_state->visual->type_3d; visual_2d_flush_hybgl_canvas(visual, txh, ctx, tr_state); tr_state->visual->type_3d = 4; tr_state->appear = ctx->appear; if (ctx->col_mat) gf_cmx_copy(&tr_state->color_mat, ctx->col_mat);// tr_state->traversing_mode=TRAVERSE_DRAW_3D; gf_mx_from_mx2d(&tr_state->model_matrix, &ctx->transform); clipper.x = INT2FIX(ctx->bi->clip.x); clipper.y = INT2FIX(ctx->bi->clip.y); clipper.width = INT2FIX(ctx->bi->clip.width); clipper.height = INT2FIX(ctx->bi->clip.height); visual_3d_set_clipper_2d(tr_state->visual, clipper, NULL, 1); gf_node_allow_cyclic_traverse(ctx->drawable->node); gf_node_traverse(ctx->drawable->node, tr_state); tr_state->visual->type_3d=prev_type_3d; tr_state->traversing_mode=prev_mode; if (ctx->col_mat) gf_cmx_init(&tr_state->color_mat); ctx->flags |= CTX_PATH_FILLED; visual_3d_reset_clipper_2d(tr_state->visual); }
void visual_2d_setup_projection(GF_VisualManager *visual, GF_TraverseState *tr_state) { GF_Rect rc; tr_state->visual = visual; #ifndef GPAC_DISABLE_VRML tr_state->backgrounds = visual->back_stack; tr_state->viewpoints = visual->view_stack; #endif /*setup clipper*/ if (visual->center_coords) { if (!visual->offscreen) { if (visual->compositor->scalable_zoom) rc = gf_rect_center(INT2FIX(visual->compositor->display_width), INT2FIX(visual->compositor->display_height)); else rc = gf_rect_center(INT2FIX(visual->compositor->output_width + 2*visual->compositor->vp_x), INT2FIX(visual->compositor->output_height + 2*visual->compositor->vp_y)); } else { rc = gf_rect_center(INT2FIX(visual->width), INT2FIX(visual->height)); } } else { rc.x = 0; rc.width = INT2FIX(visual->width); rc.y = rc.height = INT2FIX(visual->height); } /*set top-transform to pixelMetrics*/ if (!tr_state->pixel_metrics) gf_mx2d_add_scale(&tr_state->transform, tr_state->min_hsize, tr_state->min_hsize); visual->surf_rect = gf_rect_pixelize(&rc); // 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)); /*setup top clipper*/ if (visual->center_coords) { rc = gf_rect_center(INT2FIX(visual->width), INT2FIX(visual->height)); } else { rc.width = INT2FIX(visual->width); rc.height = INT2FIX(visual->height); rc.x = 0; rc.y = rc.height; if (visual->compositor->visual==visual) { rc.x += INT2FIX(visual->compositor->vp_x); rc.y += INT2FIX(visual->compositor->vp_y); } } /*setup viewport*/ #ifndef GPAC_DISABLE_VRML if (gf_list_count(visual->view_stack)) { tr_state->traversing_mode = TRAVERSE_BINDABLE; tr_state->bounds = rc; gf_node_traverse((GF_Node *) gf_list_get(visual->view_stack, 0), tr_state); } #endif visual->top_clipper = gf_rect_pixelize(&rc); tr_state->clipper = rc; // 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)); }
static void l3d_CheckBindables(GF_Node *n, GF_TraverseState *tr_state, Bool force_traverse) { GF_Node *btop; u32 mode; M_Layer3D *l3d; l3d = (M_Layer3D *)n; mode = tr_state->traversing_mode; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; if (force_traverse) gf_node_traverse(l3d->background, tr_state); btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); if (btop != l3d->background) { gf_node_unregister(l3d->background, n); gf_node_register(btop, n); l3d->background = btop; gf_node_event_out_str(n, "background"); } if (force_traverse) gf_node_traverse(l3d->viewpoint, tr_state); btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); if (btop != l3d->viewpoint) { gf_node_unregister(l3d->viewpoint, n); gf_node_register(btop, n); l3d->viewpoint = btop; gf_node_event_out_str(n, "viewpoint"); } if (force_traverse) gf_node_traverse(l3d->navigationInfo, tr_state); btop = (GF_Node*)gf_list_get(tr_state->navigations, 0); if (btop != l3d->navigationInfo) { gf_node_unregister(l3d->navigationInfo, n); gf_node_register(btop, n); l3d->navigationInfo = btop; gf_node_event_out_str(n, "navigationInfo"); } if (force_traverse) gf_node_traverse(l3d->fog, tr_state); btop = (GF_Node*)gf_list_get(tr_state->fogs, 0); if (btop != l3d->fog) { gf_node_unregister(l3d->fog, n); gf_node_register(btop, n); l3d->fog = btop; gf_node_event_out_str(n, "fog"); } tr_state->traversing_mode = mode; }
static void TraverseNonLinearDeformer(GF_Node *n, void *rs, Bool is_destroy) { GF_TraverseState *tr_state = (GF_TraverseState *)rs; /*traverse geometry for get_bounds to make sure geometry is up to date*/ if (!is_destroy && (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS)) { gf_node_traverse(((M_NonLinearDeformer*)n)->geometry, tr_state); } drawable_3d_base_traverse(n, rs, is_destroy, build_shape_nld); }
void visual_2d_pick_node(GF_VisualManager *visual, GF_TraverseState *tr_state, GF_Event *ev, GF_ChildNodeItem *children) { GF_Matrix2D backup; visual->bounds_tracker_modif_flag = DRAWABLE_HAS_CHANGED_IN_LAST_TRAVERSE; gf_mx2d_copy(backup, tr_state->transform); visual_2d_setup_projection(visual, tr_state); visual->compositor->hit_node = NULL; tr_state->ray.orig.x = INT2FIX(ev->mouse.x); tr_state->ray.orig.y = INT2FIX(ev->mouse.y); tr_state->ray.orig.z = 0; tr_state->ray.dir.x = 0; tr_state->ray.dir.y = 0; tr_state->ray.dir.z = -FIX_ONE; visual->compositor->hit_world_point = tr_state->ray.orig; visual->compositor->hit_world_ray = tr_state->ray; visual->compositor->hit_square_dist = 0; gf_list_reset(visual->compositor->sensors); tr_state->traversing_mode = TRAVERSE_PICK; /*not the root scene, use children list*/ if (visual->compositor->visual != visual) { while (children) { gf_node_traverse(children->node, tr_state); children = children->next; } } else { u32 i = 0; GF_SceneGraph *sg = visual->compositor->scene; GF_Node *root = gf_sg_get_root_node(sg); gf_node_traverse(root, tr_state); while ((sg = (GF_SceneGraph*)gf_list_enum(visual->compositor->extra_scenes, &i))) { gf_sc_traverse_subscene(visual->compositor, root, sg, tr_state); } } gf_mx2d_copy(tr_state->transform, backup); }
static void l2d_CheckBindables(GF_Node *n, GF_TraverseState *tr_state, Bool force_traverse) { GF_Node *btop; M_Layer2D *l2d; l2d = (M_Layer2D *)n; if (force_traverse) gf_node_traverse(l2d->background, tr_state); btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); if (btop != l2d->background) { gf_node_unregister(l2d->background, n); gf_node_register(btop, n); l2d->background = btop; gf_node_event_out_str(n, "background"); } if (force_traverse) gf_node_traverse(l2d->viewport, tr_state); btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); if (btop != l2d->viewport) { gf_node_unregister(l2d->viewport, n); gf_node_register(btop, n); l2d->viewport = btop; gf_node_event_out_str(n, "viewport"); } }
static void TraverseBitWrapper(GF_Node *node, void *rs, Bool is_destroy) { GF_TraverseState *tr_state; M_BitWrapper *bitWrap; if (is_destroy) { gf_node_set_private(node, NULL); return; } tr_state = (GF_TraverseState *)rs; // Traverse the node here bitWrap = (M_BitWrapper *)node; gf_node_traverse(bitWrap->node, tr_state); }
static void TraversePathExtrusion(GF_Node *node, void *rs, Bool is_destroy) { PathExtrusion path_ext; Drawable *stack_2d; GF_TraverseState *tr_state = (GF_TraverseState *)rs; Drawable3D *stack = (Drawable3D *)gf_node_get_private(node); if (is_destroy) { drawable_3d_del(node); return; } if (!PathExtrusion_GetNode(node, &path_ext)) return; if (!path_ext.geometry) return; if (gf_node_dirty_get(node)) { u32 mode = tr_state->traversing_mode; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; gf_node_traverse(path_ext.geometry, tr_state); tr_state->traversing_mode = mode; switch (gf_node_get_tag(path_ext.geometry) ) { case TAG_MPEG4_Circle: case TAG_MPEG4_Ellipse: case TAG_MPEG4_Rectangle: case TAG_MPEG4_Curve2D: case TAG_MPEG4_XCurve2D: case TAG_MPEG4_IndexedFaceSet2D: case TAG_MPEG4_IndexedLineSet2D: stack_2d = (Drawable*)gf_node_get_private(path_ext.geometry); if (!stack_2d) return; mesh_extrude_path(stack->mesh, stack_2d->path, path_ext.spine, path_ext.creaseAngle, path_ext.beginCap, path_ext.endCap, path_ext.orientation, path_ext.scale, path_ext.txAlongSpine); break; case TAG_MPEG4_Text: compositor_extrude_text(path_ext.geometry, tr_state, stack->mesh, path_ext.spine, path_ext.creaseAngle, path_ext.beginCap, path_ext.endCap, path_ext.orientation, path_ext.scale, path_ext.txAlongSpine); break; } } if (tr_state->traversing_mode==TRAVERSE_DRAW_3D) { visual_3d_draw(tr_state, stack->mesh); } else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { tr_state->bbox = stack->mesh->bounds; } }
static void TraverseLOD(GF_Node *node, void *rs, Bool is_destroy) { GF_ChildNodeItem *children; MFFloat *ranges; SFVec3f pos, usr; u32 which_child, nb_children; Fixed dist; Bool do_all; GF_Matrix mx; SFVec3f center; GF_TraverseState *tr_state = (GF_TraverseState *)rs; s32 *prev_child = (s32 *)gf_node_get_private(node); if (is_destroy) { gf_free(prev_child); gf_sc_check_focus_upon_destroy(node); return; } /*WARNING: X3D/MPEG4 NOT COMPATIBLE*/ if (gf_node_get_tag(node) == TAG_MPEG4_LOD) { children = ((M_LOD *) node)->level; ranges = &((M_LOD *) node)->range; center = ((M_LOD *) node)->center; #ifndef GPAC_DISABLE_X3D } else { children = ((X_LOD *) node)->children; ranges = &((X_LOD *) node)->range; center = ((X_LOD *) node)->center; #endif } if (!children) return; nb_children = gf_node_list_get_count(children); if (!tr_state->camera) { do_all = 1; which_child = 0; } else { /*can't cache the matrix here*/ usr = tr_state->camera->position; pos = center; gf_mx_copy(mx, tr_state->model_matrix); gf_mx_inverse(&mx); gf_mx_apply_vec(&mx, &usr); gf_vec_diff(pos, pos, usr); dist = gf_vec_len(pos); for (which_child=0; which_child<ranges->count; which_child++) { if (dist<ranges->vals[which_child]) break; } if (which_child>=nb_children) which_child = nb_children-1; /*check if we're traversing the same child or not for audio rendering*/ do_all = 0; if (gf_node_dirty_get(node)) { gf_node_dirty_clear(node, 0); do_all = 1; } else if ((s32) which_child != *prev_child) { *prev_child = which_child; do_all = 1; } } if (do_all) { u32 i; Bool prev_switch = tr_state->switched_off; GF_ChildNodeItem *l = children; tr_state->switched_off = 1; i=0; while (l) { if (i!=which_child) gf_node_traverse(l->node, rs); l = l->next; } tr_state->switched_off = prev_switch; } gf_node_traverse(gf_node_list_get_child(children, which_child), rs); }
/* Shape */ static void TraverseShape(GF_Node *node, void *rs, Bool is_destroy) { GF_TraverseState *tr_state; M_Shape *shape; if (is_destroy ) return; tr_state = (GF_TraverseState *)rs; #ifndef GPAC_DISABLE_3D if (tr_state->traversing_mode==TRAVERSE_LIGHTING) return; #endif shape = (M_Shape *) node; if (!shape->geometry) return; /*reset this node dirty flag (because bitmap may trigger bounds invalidation on the fly)*/ gf_node_dirty_clear(node, 0); /*check traverse mode, and take care of switch-off flag*/ if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { GF_Node *m; tr_state->appear = (GF_Node *) shape->appearance; /*this is done regardless of switch flag*/ gf_node_traverse((GF_Node *) shape->geometry, tr_state); if (tr_state->appear) { /*apply line width*/ m = ((M_Appearance *)tr_state->appear)->material; if (m && (gf_node_get_tag(m)==TAG_MPEG4_Material2D) ) { DrawAspect2D asp; Fixed width = 0; asp.line_scale = FIX_ONE; m = ((M_Material2D *)m)->lineProps; if (m) { switch (gf_node_get_tag(m)) { case TAG_MPEG4_LineProperties: width = ((M_LineProperties *) m)->width; drawable_compute_line_scale(tr_state, &asp); break; case TAG_MPEG4_XLineProperties: if ( ((M_XLineProperties *) m)->isCenterAligned) width = ((M_XLineProperties *) m)->width; if ( ((M_XLineProperties *) m)->isScalable) drawable_compute_line_scale(tr_state, &asp); break; } width = gf_mulfix(width, asp.line_scale); tr_state->bounds.width += width; tr_state->bounds.height += width; tr_state->bounds.y += width/2; tr_state->bounds.x -= width/2; } } tr_state->appear = NULL; } } else { if (tr_state->switched_off) return; tr_state->appear = (GF_Node *) shape->appearance; switch (tr_state->traversing_mode) { case TRAVERSE_SORT: #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) visual_3d_register_context(tr_state, shape->geometry); else #endif gf_node_traverse((GF_Node *) shape->geometry, tr_state); break; case TRAVERSE_PICK: gf_node_traverse((GF_Node *) shape->geometry, tr_state); break; #ifndef GPAC_DISABLE_3D /*if we're here we passed culler already*/ case TRAVERSE_DRAW_3D: gf_node_traverse((GF_Node *) shape->geometry, tr_state); break; case TRAVERSE_COLLIDE: visual_3d_vrml_drawable_collide(shape->geometry, tr_state); break; #endif } tr_state->appear = NULL; } }
static void svg_traverse_text(GF_Node *node, void *rs, Bool is_destroy) { SVGPropertiesPointers backup_props; u32 backup_flags; GF_Matrix2D backup_matrix; GF_Matrix mx3d; GF_ChildNodeItem *child; DrawableContext *ctx; SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVG_Element *text = (SVG_Element *)node; SVGAllAttributes atts; u32 i,imax; Fixed * lw; if (is_destroy) { drawable_del(st->drawable); svg_reset_text_stack(st); gf_list_del(st->spans); gf_free(st); return; } if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { svg_text_draw_2d(st, tr_state); return; } else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { tr_state->text_parent = node; gf_font_spans_get_selection(node, st->spans, tr_state); /*and browse children*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; } child = child->next; } tr_state->text_parent = NULL; return; } gf_svg_flatten_attributes(text, &atts); if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) return; tr_state->in_svg_text++; tr_state->text_parent = node; if (tr_state->traversing_mode==TRAVERSE_PICK) { compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, 1, st->drawable); /*and browse children*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; } child = child->next; } memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); tr_state->svg_flags = backup_flags; tr_state->text_parent = NULL; tr_state->in_svg_text--; return; } else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { gf_font_spans_get_selection(node, st->spans, tr_state); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; tr_state->text_parent = NULL; tr_state->in_svg_text--; return; } compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); if ( (st->prev_size != tr_state->svg_props->font_size->value) || (st->prev_flags != *tr_state->svg_props->font_style) || (st->prev_anchor != *tr_state->svg_props->text_anchor) || (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) ) || tr_state->visual->compositor->reset_fonts ) { u32 mode; child = ((GF_ParentNode *) text)->children; svg_reset_text_stack(st); tr_state->text_end_x = 0; tr_state->text_end_y = 0; /*init the xml:space algo*/ tr_state->last_char_type = 0; /*initialize x and y counters - stored at the traverse level for handling tspan & co*/ if (atts.text_x) tr_state->count_x = gf_list_count(*atts.text_x); else tr_state->count_x=0; if (atts.text_y) tr_state->count_y = gf_list_count(*atts.text_y); else tr_state->count_y=0; if (atts.text_rotate) tr_state->count_rotate = gf_list_count(*atts.text_rotate); else tr_state->count_rotate=0; /*horizontal justifiers container*/ tr_state->x_anchors = gf_list_new(); /*compute length of all text blocks*/ while (child) { svg_compute_text_width(child->node, &atts, tr_state); child=child->next; } /*apply justification of all blocks*/ imax=gf_list_count(tr_state->x_anchors); for (i=0; i<imax; i++) { lw=gf_list_get(tr_state->x_anchors, i); svg_apply_text_anchor(tr_state, lw); } /*re-initialize x and y counters for final compute*/ if (atts.text_x) tr_state->count_x = gf_list_count(*atts.text_x); else tr_state->count_x=0; if (atts.text_y) tr_state->count_y = gf_list_count(*atts.text_y); else tr_state->count_y=0; if (atts.text_rotate) tr_state->count_rotate = gf_list_count(*atts.text_rotate); else tr_state->count_rotate=0; tr_state->idx_rotate = 0; tr_state->chunk_index = 0; /*initialize current text position*/ if (!tr_state->text_end_x) { SVG_Coordinate *xc = (atts.text_x ? (SVG_Coordinate *) gf_list_get(*atts.text_x, 0) : NULL); tr_state->text_end_x = (xc ? xc->value : 0); } if (!tr_state->text_end_y) { SVG_Coordinate *yc = (atts.text_y ? (SVG_Coordinate *) gf_list_get(*atts.text_y, 0) : NULL); tr_state->text_end_y = (yc ? yc->value : 0); } /*pass x and y to children*/ tr_state->text_x = atts.text_x; tr_state->text_y = atts.text_y; tr_state->text_rotate = atts.text_rotate; drawable_reset_path(st->drawable); /*switch to bounds mode, and recompute children*/ mode = tr_state->traversing_mode; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; tr_state->last_char_type = 0; child = ((GF_ParentNode *) text)->children; while (child) { svg_traverse_text_block(child->node, &atts, tr_state, st->spans); child = child->next; } tr_state->traversing_mode = mode; gf_node_dirty_clear(node, 0); drawable_mark_modified(st->drawable, tr_state); st->prev_size = tr_state->svg_props->font_size->value; st->prev_flags = *tr_state->svg_props->font_style; st->prev_anchor = *tr_state->svg_props->text_anchor; while (gf_list_count(tr_state->x_anchors)) { Fixed *f = gf_list_last(tr_state->x_anchors); gf_list_rem_last(tr_state->x_anchors); gf_free(f); } gf_list_del(tr_state->x_anchors); tr_state->x_anchors = NULL; svg_update_bounds(st); } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { if (!compositor_svg_is_display_off(tr_state->svg_props)) tr_state->bounds = st->bounds; } else if ((tr_state->traversing_mode == TRAVERSE_SORT) && !compositor_svg_is_display_off(tr_state->svg_props) && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { ctx = drawable_init_context_svg(st->drawable, tr_state); if (ctx) svg_finalize_sort(ctx, st, tr_state); /*and browse children*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; case TAG_SVG_switch: gf_node_traverse(child->node, tr_state); break; } child = child->next; } } tr_state->in_svg_text--; tr_state->text_parent = NULL; compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
/*we have no choice but always browsing the children, since a src can be replaced by a new one without the parent being modified. We just collect the src and check against the current mixer inputs to reset the mixer or not - the spec is not clear about that btw, shall rebuffering happen if a source is modified or not ...*/ static void audiobuffer_traverse(GF_Node *node, void *rs, Bool is_destroy) { u32 j; Bool update_mixer; GF_ChildNodeItem *l; GF_AudioGroup *parent; AudioBufferStack *st = (AudioBufferStack *)gf_node_get_private(node); M_AudioBuffer *ab = (M_AudioBuffer *)node; GF_TraverseState*tr_state = (GF_TraverseState*) rs; if (is_destroy) { gf_sc_audio_unregister(&st->output); if (st->time_handle.is_registered) gf_sc_unregister_time_node(st->output.compositor, &st->time_handle); gf_mixer_del(st->am); if (st->buffer) gf_free(st->buffer); gf_list_del(st->new_inputs); gf_free(st); return; } parent = tr_state->audio_parent; tr_state->audio_parent = (GF_AudioGroup *) st; l = ab->children; while (l) { gf_node_traverse(l->node, tr_state); l = l->next; } gf_mixer_lock(st->am, 1); /*if no new inputs don't change mixer config*/ update_mixer = gf_list_count(st->new_inputs) ? 1 : 0; if (gf_mixer_get_src_count(st->am) == gf_list_count(st->new_inputs)) { u32 count = gf_list_count(st->new_inputs); update_mixer = 0; for (j=0; j<count; j++) { GF_AudioInput *cur = (GF_AudioInput *)gf_list_get(st->new_inputs, j); if (!gf_mixer_is_src_present(st->am, &cur->input_ifce)) { update_mixer = 1; break; } } } if (update_mixer) { gf_mixer_remove_all(st->am); gf_mixer_force_chanel_out(st->am, ab->numChan); } while (gf_list_count(st->new_inputs)) { GF_AudioInput *src = (GF_AudioInput *)gf_list_get(st->new_inputs, 0); gf_list_rem(st->new_inputs, 0); if (update_mixer) gf_mixer_add_input(st->am, &src->input_ifce); } gf_mixer_lock(st->am, 0); tr_state->audio_parent = parent; /*Note the audio buffer is ALWAYS registered untill destroyed since buffer filling shall happen even when inactive*/ if (!st->output.register_with_parent || !st->output.register_with_renderer) gf_sc_audio_register(&st->output, tr_state); /*store mute flag*/ st->is_muted = tr_state->switched_off; }
/* This is a crude draft implementation of filter. The main drawback is that we don't cache any data. We should be able to check for changes in the sub-group or in the filter */ void svg_draw_filter(GF_Node *filter, GF_Node *node, GF_TraverseState *tr_state) { GF_IRect rc1, rc2; #ifndef GPAC_DISABLE_3D u32 type_3d; #endif u32 prev_flags; GF_IRect txrc; Fixed scale_x, scale_y, temp_x, temp_y; DrawableContext *ctx, *child_ctx; GF_SURFACE offscreen_surface, old_surf; GF_Rect bounds, local_bounds, rc; GF_Matrix2D backup; SVGAllAttributes all_atts; GF_FilterStack *st = gf_node_get_private(filter); assert(tr_state->traversing_mode==TRAVERSE_SORT); /*store the current transform matrix, create a new one for group_cache*/ gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_init(tr_state->transform); gf_node_allow_cyclic_traverse(node); tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; tr_state->bounds.width = tr_state->bounds.height = 0; gf_node_traverse(node, tr_state); local_bounds = bounds = tr_state->bounds; /*compute bounds in final coordinate system - this ensures that the cache has the correct anti aliasing*/ gf_mx2d_apply_rect(&backup, &bounds); txrc = gf_rect_pixelize(&bounds); if (txrc.width%2) txrc.width++; if (txrc.height%2) txrc.height++; bounds = gf_rect_ft(&txrc); tr_state->traversing_mode = TRAVERSE_SORT; gf_mx2d_copy(tr_state->transform, backup); if (!bounds.width || !bounds.height) { return; } /*create a context */ ctx = drawable_init_context_svg(st->drawable, tr_state); if (!ctx) return; /*setup texture */ st->txh.height = txrc.height; st->txh.width = txrc.width; st->txh.stride = txrc.width * 4; st->txh.pixelformat = GF_PIXEL_ARGB; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) st->txh.pixelformat = GF_PIXEL_RGBA; #endif st->txh.transparent = 1; if (st->txh.stride * st->txh.height > st->alloc_size) { st->alloc_size = st->txh.stride * st->txh.height; st->data = (u8*)gf_realloc(st->data, sizeof(u8) * st->alloc_size); } memset(st->data, 0x0, sizeof(char) * st->txh.stride * st->txh.height); st->txh.data = (char *) st->data; /*setup geometry (rectangle matching the bounds of the object) Warning, we want to center the cached bitmap at the center of the screen (main visual)*/ gf_path_reset(st->drawable->path); gf_path_add_rect_center(st->drawable->path, bounds.x + bounds.width/2, bounds.y - bounds.height/2, bounds.width, bounds.height); old_surf = tr_state->visual->raster_surface; offscreen_surface = tr_state->visual->compositor->rasterizer->surface_new(tr_state->visual->compositor->rasterizer, tr_state->visual->center_coords); tr_state->visual->raster_surface = offscreen_surface; gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_init(tr_state->transform); /*attach the buffer to visual*/ tr_state->visual->compositor->rasterizer->surface_attach_to_buffer(offscreen_surface, st->txh.data, st->txh.width, st->txh.height, 0, st->txh.stride, st->txh.pixelformat); prev_flags = tr_state->immediate_draw; tr_state->immediate_draw = 1; tr_state->traversing_mode = TRAVERSE_SORT; tr_state->in_svg_filter = 1; /*recompute the bounds with the final scaling used*/ scale_x = gf_divfix(bounds.width, local_bounds.width); scale_y = gf_divfix(bounds.height, local_bounds.height); gf_mx2d_init(tr_state->transform); gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y); rc = local_bounds; gf_mx2d_apply_rect(&tr_state->transform, &rc); /*centered the bitmap on the visual*/ if (tr_state->visual->center_coords) { temp_x = -rc.x - rc.width/2; temp_y = rc.height/2 - rc.y; } else { temp_x = -rc.x; temp_y = rc.height - rc.y; } gf_mx2d_add_translation(&tr_state->transform, temp_x, temp_y); rc1 = tr_state->visual->surf_rect; rc2 = tr_state->visual->top_clipper; tr_state->visual->surf_rect.width = st->txh.width; tr_state->visual->surf_rect.height = st->txh.height; if (tr_state->visual->center_coords) { tr_state->visual->surf_rect.y = st->txh.height/2; tr_state->visual->surf_rect.x = -1 * (s32) st->txh.width/2; } else { tr_state->visual->surf_rect.y = st->txh.height; tr_state->visual->surf_rect.x = 0; } tr_state->visual->top_clipper = tr_state->visual->surf_rect; #ifndef GPAC_DISABLE_3D type_3d = tr_state->visual->type_3d; tr_state->visual->type_3d=0; #endif if (prev_flags) gf_node_allow_cyclic_traverse(node); gf_node_traverse(node, tr_state); child_ctx = ctx->next; while (child_ctx && child_ctx->drawable) { drawable_reset_bounds(child_ctx->drawable, tr_state->visual); child_ctx->drawable = NULL; child_ctx = child_ctx->next; } tr_state->visual->cur_context = ctx; /*restore state and destroy whatever needs to be cleaned*/ tr_state->in_svg_filter = 0; tr_state->immediate_draw = prev_flags; tr_state->visual->compositor->rasterizer->surface_delete(offscreen_surface); tr_state->visual->raster_surface = old_surf; tr_state->traversing_mode = TRAVERSE_SORT; tr_state->visual->surf_rect = rc1; tr_state->visual->top_clipper = rc2; #ifndef GPAC_DISABLE_3D tr_state->visual->type_3d = type_3d ; #endif /*update texture*/ st->txh.transparent = 1; st->txh.flags |= GF_SR_TEXTURE_NO_GL_FLIP; gf_sc_texture_set_data(&st->txh); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) gf_sc_texture_push_image(&st->txh, 0, 0); else #endif gf_sc_texture_push_image(&st->txh, 0, 1); ctx->flags |= CTX_NO_ANTIALIAS; ctx->aspect.fill_color = 0; ctx->aspect.line_color = 0xFF000000; ctx->aspect.fill_texture = &st->txh; ctx->flags |= CTX_TEXTURE_DIRTY; /*get the filter region*/ bounds = local_bounds; gf_svg_flatten_attributes((SVG_Element *)filter, &all_atts); if (!all_atts.filterUnits || (*all_atts.filterUnits==SVG_GRADIENTUNITS_OBJECT)) { Fixed v; v = all_atts.x ? all_atts.x->value : INT2FIX(-10); bounds.x += gf_mulfix(v, bounds.width); v = all_atts.y ? all_atts.y->value : INT2FIX(-10); bounds.y += gf_mulfix(v, bounds.height); v = all_atts.width ? all_atts.width->value : INT2FIX(120); bounds.width = gf_mulfix(v, bounds.width); v = all_atts.height ? all_atts.height->value : INT2FIX(120); bounds.height = gf_mulfix(v, bounds.height); } else { bounds.x = all_atts.x ? all_atts.x->value : 0; bounds.y = all_atts.y ? all_atts.y->value : 0; bounds.width = all_atts.width ? all_atts.x->value : bounds.width; bounds.height = all_atts.x ? all_atts.x->value : 120; } gf_mx2d_apply_rect(&backup, &bounds); svg_filter_apply(filter, &st->txh, &bounds); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (!st->drawable->mesh) { st->drawable->mesh = new_mesh(); mesh_from_path(st->drawable->mesh, st->drawable->path); } visual_3d_draw_from_context(tr_state->ctx, tr_state); ctx->drawable = NULL; return; } #endif /*we computed the texture in final screen coordinate, so use the identity matrix for the context*/ gf_mx2d_init(tr_state->transform); drawable_finalize_sort(ctx, tr_state, NULL); gf_mx2d_copy(tr_state->transform, backup); }
Bool group_cache_traverse(GF_Node *node, GroupCache *cache, GF_TraverseState *tr_state, Bool force_recompute, Bool is_mpeg4, Bool auto_fit_vp) { GF_Matrix2D backup; DrawableContext *group_ctx = NULL; GF_ChildNodeItem *l; if (!cache) return 0; /*do we need to recompute the cache*/ if (cache->force_recompute) { force_recompute = 1; cache->force_recompute = 0; } else if (gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY) { force_recompute = 1; } /*we need to redraw the group in an offscreen visual*/ if (force_recompute) { GF_Matrix2D backup; GF_IRect rc1, rc2; u32 type_3d; u32 prev_flags; GF_Rect cache_bounds; GF_SURFACE offscreen_surface, old_surf; GF_Raster2D *r2d = tr_state->visual->compositor->rasterizer; DrawableContext *child_ctx; Fixed temp_x, temp_y, scale_x, scale_y; GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Recomputing cache for subtree %s\n", gf_node_get_log_name(node))); /*step 1 : store current state and indicate children should not be cached*/ tr_state->in_group_cache = 1; prev_flags = tr_state->immediate_draw; /*store the current transform matrix, create a new one for group_cache*/ gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_init(tr_state->transform); type_3d = 0; #ifndef GPAC_DISABLE_3D /*force 2D rendering*/ type_3d = tr_state->visual->type_3d; tr_state->visual->type_3d = 0; #endif /*step 2: collect the bounds of all children*/ tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; cache_bounds.width = cache_bounds.height = 0; l = ((GF_ParentNode*)node)->children; while (l) { tr_state->bounds.width = tr_state->bounds.height = 0; gf_node_traverse(l->node, tr_state); l = l->next; gf_rect_union(&cache_bounds, &tr_state->bounds); } tr_state->traversing_mode = TRAVERSE_SORT; if (!cache_bounds.width || !cache_bounds.height) { tr_state->in_group_cache = 0; tr_state->immediate_draw = prev_flags; gf_mx2d_copy(tr_state->transform, backup); #ifndef GPAC_DISABLE_3D tr_state->visual->type_3d = type_3d; #endif return 0; } /*step 3: insert a DrawableContext for this group in the display list*/ if (is_mpeg4) { #ifndef GPAC_DISABLE_VRML group_ctx = drawable_init_context_mpeg4(cache->drawable, tr_state); #endif } else { #ifndef GPAC_DISABLE_SVG group_ctx = drawable_init_context_svg(cache->drawable, tr_state); #endif } if (!group_ctx) return 0; /*step 4: now we have the bounds: allocate the offscreen memory create temp raster visual & attach to buffer override the tr_state->visual->the_surface with the temp raster add translation (shape is not always centered) setup top clipers */ old_surf = tr_state->visual->raster_surface; offscreen_surface = r2d->surface_new(r2d, tr_state->visual->center_coords); /*a new temp raster visual*/ tr_state->visual->raster_surface = offscreen_surface; /*use current surface coordinate scaling to compute the cache*/ #ifdef GF_SR_USE_VIDEO_CACHE scale_x = tr_state->visual->compositor->cache_scale * backup.m[0] / 100; scale_y = tr_state->visual->compositor->cache_scale * backup.m[4] / 100; #else scale_x = backup.m[0]; scale_y = backup.m[4]; #endif if (scale_x<0) scale_x = -scale_x; if (scale_y<0) scale_y = -scale_y; cache->scale = MAX(scale_x, scale_y); tr_state->bounds = cache_bounds; gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y); gf_mx2d_apply_rect(&tr_state->transform, &cache_bounds); rc1 = gf_rect_pixelize(&cache_bounds); if (rc1.width % 2) rc1.width++; if (rc1.height%2) rc1.height++; /* Initialize the group cache with the scaled pixelized bounds for texture but the original bounds for path*/ group_cache_setup(cache, &tr_state->bounds, &rc1, tr_state->visual->compositor, type_3d); /*attach the buffer to visual*/ r2d->surface_attach_to_buffer(offscreen_surface, cache->txh.data, cache->txh.width, cache->txh.height, 0, cache->txh.stride, cache->txh.pixelformat); /*recompute the bounds with the final scaling used*/ scale_x = gf_divfix(INT2FIX(rc1.width), tr_state->bounds.width); scale_y = gf_divfix(INT2FIX(rc1.height), tr_state->bounds.height); gf_mx2d_init(tr_state->transform); gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y); cache_bounds = tr_state->bounds; gf_mx2d_apply_rect(&tr_state->transform, &cache_bounds); /*centered the bitmap on the visual*/ temp_x = -cache_bounds.x; temp_y = -cache_bounds.y; if (tr_state->visual->center_coords) { temp_x -= cache_bounds.width/2; temp_y += cache_bounds.height/2; } else { temp_y += cache_bounds.height; } gf_mx2d_add_translation(&tr_state->transform, temp_x, temp_y); /*override top clippers*/ rc1 = tr_state->visual->surf_rect; rc2 = tr_state->visual->top_clipper; tr_state->visual->surf_rect.width = cache->txh.width; tr_state->visual->surf_rect.height = cache->txh.height; if (tr_state->visual->center_coords) { tr_state->visual->surf_rect.y = cache->txh.height/2; tr_state->visual->surf_rect.x = -1 * (s32) cache->txh.width/2; } else { tr_state->visual->surf_rect.y = cache->txh.height; tr_state->visual->surf_rect.x = 0; } tr_state->visual->top_clipper = tr_state->visual->surf_rect; /*step 5: traverse subtree in direct draw mode*/ tr_state->immediate_draw = 1; group_ctx->flags &= ~CTX_NO_ANTIALIAS; l = ((GF_ParentNode*)node)->children; while (l) { gf_node_traverse(l->node, tr_state); l = l->next; } /*step 6: reset all contexts after the current group one*/ child_ctx = group_ctx->next; while (child_ctx && child_ctx->drawable) { drawable_reset_bounds(child_ctx->drawable, tr_state->visual); child_ctx->drawable = NULL; child_ctx = child_ctx->next; } /*and set ourselves as the last context on the main visual*/ tr_state->visual->cur_context = group_ctx; /*restore state and destroy whatever needs to be cleaned*/ gf_mx2d_copy(tr_state->transform, backup); tr_state->in_group_cache = 0; tr_state->immediate_draw = prev_flags; r2d->surface_delete(offscreen_surface); tr_state->visual->raster_surface = old_surf; tr_state->traversing_mode = TRAVERSE_SORT; #ifndef GPAC_DISABLE_3D tr_state->visual->type_3d = type_3d; #endif tr_state->visual->surf_rect = rc1; tr_state->visual->top_clipper = rc2; /*update texture*/ cache->txh.transparent = 1; cache->txh.flags |= GF_SR_TEXTURE_NO_GL_FLIP; gf_sc_texture_set_data(&cache->txh); gf_sc_texture_push_image(&cache->txh, 0, type_3d ? 0 : 1); cache->orig_vp = tr_state->vp_size; } /*just setup the context*/ else { if (is_mpeg4) { #ifndef GPAC_DISABLE_VRML group_ctx = drawable_init_context_mpeg4(cache->drawable, tr_state); #endif } else { #ifndef GPAC_DISABLE_SVG group_ctx = drawable_init_context_svg(cache->drawable, tr_state); #endif } } if (!group_ctx) return 0; group_ctx->flags |= CTX_NO_ANTIALIAS; if (cache->opacity != FIX_ONE) group_ctx->aspect.fill_color = GF_COL_ARGB_FIXED(cache->opacity, FIX_ONE, FIX_ONE, FIX_ONE); else group_ctx->aspect.fill_color = 0; group_ctx->aspect.fill_texture = &cache->txh; if (!cache->opacity) { group_ctx->drawable = NULL; return 0; } if (gf_node_dirty_get(node)) group_ctx->flags |= CTX_TEXTURE_DIRTY; #ifdef CACHE_DEBUG_CENTER gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_init(tr_state->transform); #else gf_mx2d_copy(backup, tr_state->transform); if (auto_fit_vp) { if ((tr_state->vp_size.x != cache->orig_vp.x) || (tr_state->vp_size.y != cache->orig_vp.y)) { GF_Matrix2D m; gf_mx2d_init(m); gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_add_scale(&m, gf_divfix(tr_state->vp_size.x, cache->orig_vp.x), gf_divfix(tr_state->vp_size.y, cache->orig_vp.y) ); gf_mx2d_pre_multiply(&tr_state->transform, &m); } else { auto_fit_vp = 0; } } #endif #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (!cache->drawable->mesh) { cache->drawable->mesh = new_mesh(); mesh_from_path(cache->drawable->mesh, cache->drawable->path); } visual_3d_draw_from_context(group_ctx, tr_state); group_ctx->drawable = NULL; } else #endif drawable_finalize_sort(group_ctx, tr_state, NULL); #ifndef CACHE_DEBUG_CENTER if (auto_fit_vp) #endif { gf_mx2d_copy(tr_state->transform, backup); } return (force_recompute==1); }
static void TraverseSwitch(GF_Node *node, void *rs, Bool is_destroy) { GF_ChildNodeItem *l; u32 i; Bool prev_switch; GF_ChildNodeItem *children; s32 whichChoice; GF_Node *child; SwitchStack *st = (SwitchStack *)gf_node_get_private(node); GF_TraverseState *tr_state; tr_state = (GF_TraverseState *)rs; children = NULL; /* souchay : be sure to be initialized, -1 seems reasonable since we check if (whichChoice>=0) */ whichChoice = -1; if (is_destroy) { gf_sc_check_focus_upon_destroy(node); gf_free(st); return; } /*WARNING: X3D/MPEG4 NOT COMPATIBLE*/ switch (gf_node_get_tag(node)) { case TAG_MPEG4_Switch: children = ((M_Switch *)node)->choice; whichChoice = ((M_Switch *)node)->whichChoice; break; #ifndef GPAC_DISABLE_X3D case TAG_X3D_Switch: children = ((X_Switch *)node)->children; whichChoice = ((X_Switch *)node)->whichChoice; break; #endif } if (tr_state->traversing_mode!=TRAVERSE_GET_BOUNDS) { prev_switch = tr_state->switched_off; /*check changes in choice field*/ if ((gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) || (st->last_switch != whichChoice) ) { tr_state->switched_off = 1; i=0; l = children; while (l) { // if ((s32) i!=whichChoice) gf_node_traverse(l->node, tr_state); if ((s32) i == st->last_switch) gf_node_traverse(l->node, tr_state); l = l->next; i++; } tr_state->switched_off = 0; st->last_switch = whichChoice; } gf_node_dirty_clear(node, 0); /*no need to check for sensors since a sensor is active for the whole parent group, that is for switch itself CSQ: switch cannot be used to switch sensors, too bad...*/ tr_state->switched_off = prev_switch; } if (!children) return; if (whichChoice==-2) { #ifndef GPAC_DISABLE_3D if (tr_state->visual->autostereo_type) { u32 idx; u32 count = gf_node_list_get_count(children); /*this should be a bit more subtle (reusing views if missing, ...)...*/ idx = tr_state->visual->current_view % count; child = (GF_Node*)gf_node_list_get_child(children, idx); gf_node_traverse(child, tr_state); return; } else #endif //GPAC_DISABLE_3D { /*fallback to first view*/ whichChoice=0; } } if (whichChoice>=0) { child = (GF_Node*)gf_node_list_get_child(children, whichChoice); gf_node_traverse(child, tr_state); } }
static void TraverseLayer2D(GF_Node *node, void *rs, Bool is_destroy) { GF_List *oldb, *oldv; GF_Node *viewport; GF_Node *back; Bool prev_layer; GF_Matrix2D backup; SFVec2f prev_vp; #ifndef GPAC_DISABLE_3D GF_Matrix mx3d; GF_List *oldf, *oldn; GF_List *node_list_backup; GF_Rect prev_clipper; Bool had_clip; #endif M_Layer2D *l = (M_Layer2D *)node; Layer2DStack *st = (Layer2DStack *) gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *) rs; if (is_destroy) { gf_list_del(st->backs); gf_list_del(st->views); group_2d_destroy(node, (GroupingNode2D*)st); gf_free(st); return; } /*layers can only be used in a 2D context*/ #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d && tr_state->camera->is_3D) return; #endif /*layer2D maintains its own stacks*/ oldb = tr_state->backgrounds; oldv = tr_state->viewpoints; tr_state->backgrounds = st->backs; tr_state->viewpoints = st->views; prev_layer = tr_state->is_layer; tr_state->is_layer = 1; #ifndef GPAC_DISABLE_3D oldf = tr_state->fogs; oldn = tr_state->navigations; tr_state->fogs = tr_state->navigations = NULL; #endif l2d_CheckBindables(node, tr_state, st->first); back = (GF_Node*)gf_list_get(st->backs, 0); viewport = (GF_Node*)gf_list_get(st->views, 0); if ((tr_state->traversing_mode == TRAVERSE_SORT) || (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS)) { /*override group bounds*/ visual_get_size_info(tr_state, &st->clip.width, &st->clip.height); /*setup bounds in local coord system*/ if (l->size.x>=0) st->clip.width = l->size.x; if (l->size.y>=0) st->clip.height = l->size.y; st->clip = gf_rect_center(st->clip.width, st->clip.height); st->bounds = st->clip; } prev_vp = tr_state->vp_size; tr_state->vp_size.x = st->clip.width; tr_state->vp_size.y = st->clip.height; switch (tr_state->traversing_mode) { case TRAVERSE_SORT: #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { tr_state->layer_clipper = compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 1); visual_3d_matrix_push(tr_state->visual); gf_mx_copy(mx3d, tr_state->model_matrix); /*setup clipping*/ visual_3d_set_clipper_2d(tr_state->visual, tr_state->layer_clipper); /*apply background BEFORE viewport*/ if (back) { tr_state->traversing_mode = TRAVERSE_BINDABLE; gf_bbox_from_rect(&tr_state->bbox, &st->clip); gf_node_traverse(back, tr_state); } /*sort all children without transform, and use current transform when flushing contexts*/ gf_mx_init(tr_state->model_matrix); /*apply viewport*/ if (viewport) { tr_state->traversing_mode = TRAVERSE_BINDABLE; tr_state->bounds = st->clip; gf_node_traverse(viewport, tr_state); visual_3d_matrix_add(tr_state->visual, tr_state->model_matrix.m); } node_list_backup = tr_state->visual->alpha_nodes_to_draw; tr_state->visual->alpha_nodes_to_draw = gf_list_new(); tr_state->traversing_mode = TRAVERSE_SORT; /*reset cull flag*/ tr_state->cull_flag = 0; group_2d_traverse(node, (GroupingNode2D *)st, tr_state); visual_3d_flush_contexts(tr_state->visual, tr_state); tr_state->traversing_mode = TRAVERSE_SORT; assert(!gf_list_count(tr_state->visual->alpha_nodes_to_draw)); gf_list_del(tr_state->visual->alpha_nodes_to_draw); tr_state->visual->alpha_nodes_to_draw = node_list_backup; visual_3d_matrix_pop(tr_state->visual); gf_mx_copy(tr_state->model_matrix, mx3d); visual_3d_reset_clipper_2d(tr_state->visual); tr_state->has_layer_clip = had_clip; if (had_clip) { tr_state->layer_clipper = prev_clipper; visual_3d_set_clipper_2d(tr_state->visual, tr_state->layer_clipper); } } else #endif { GF_IRect prev_clip; GF_Rect rc; gf_mx2d_copy(backup, tr_state->transform); prev_clip = tr_state->visual->top_clipper; rc = st->clip; /*get clipper in world coordinate*/ gf_mx2d_apply_rect(&tr_state->transform, &rc); if (viewport) { tr_state->traversing_mode = TRAVERSE_BINDABLE; tr_state->bounds = st->clip; gf_node_traverse(viewport, tr_state); #if VIEWPORT_CLIPS /*move viewport box in world coordinate*/ gf_mx2d_apply_rect(&backup, &tr_state->bounds); /*and intersect with layer clipper*/ rect_intersect(&rc, &tr_state->bounds); #endif } tr_state->visual->top_clipper = gf_rect_pixelize(&rc); gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip); tr_state->traversing_mode = TRAVERSE_SORT; if (tr_state->visual->top_clipper.width && tr_state->visual->top_clipper.height) { if (back && Bindable_GetIsBound(back) ) { DrawableContext *ctx; ctx = b2d_get_context((M_Background2D*) back, st->backs); gf_mx2d_init(ctx->transform); ctx->bi->clip = tr_state->visual->top_clipper; ctx->bi->unclip = rc; if (tr_state->immediate_draw) { tr_state->ctx = ctx; tr_state->traversing_mode = TRAVERSE_DRAW_2D; gf_node_traverse(back, tr_state); tr_state->traversing_mode = TRAVERSE_SORT; tr_state->ctx = NULL; } else { DrawableContext *back_ctx = visual_2d_get_drawable_context(tr_state->visual); gf_node_traverse(back, tr_state); back_ctx->flags = ctx->flags; back_ctx->flags &= ~CTX_IS_TRANSPARENT; back_ctx->flags |= CTX_IS_BACKGROUND; back_ctx->aspect = ctx->aspect; back_ctx->drawable = ctx->drawable; drawable_check_bounds(back_ctx, tr_state->visual); back_ctx->bi->clip = ctx->bi->clip; back_ctx->bi->unclip = ctx->bi->unclip; } /*keep track of node drawn*/ if (!(ctx->drawable->flags & DRAWABLE_REGISTERED_WITH_VISUAL) ) { struct _drawable_store *it; GF_SAFEALLOC(it, struct _drawable_store); it->drawable = ctx->drawable; if (tr_state->visual->last_prev_entry) { tr_state->visual->last_prev_entry->next = it; tr_state->visual->last_prev_entry = it; } else { tr_state->visual->prev_nodes = tr_state->visual->last_prev_entry = it; } GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layer2D] Registering new drawn node %s on visual\n", gf_node_get_class_name(it->drawable->node))); ctx->drawable->flags |= DRAWABLE_REGISTERED_WITH_VISUAL; } } group_2d_traverse(node, (GroupingNode2D *)st, tr_state); } tr_state->visual->top_clipper = prev_clip; gf_mx2d_copy(tr_state->transform, backup); } break; /*check picking - we must fall in our 2D clipper*/ case TRAVERSE_PICK: if (gf_sc_pick_in_clipper(tr_state, &st->clip)) { #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { /*apply viewport*/ if (viewport) { gf_mx_copy(mx3d, tr_state->model_matrix); tr_state->traversing_mode = TRAVERSE_BINDABLE; tr_state->bounds = st->clip; gf_node_traverse(viewport, tr_state); tr_state->traversing_mode = TRAVERSE_PICK; group_2d_traverse(node, (GroupingNode2D *)st, tr_state); gf_mx_copy(tr_state->model_matrix, mx3d); } else { group_2d_traverse(node, (GroupingNode2D *)st, tr_state); } } else #endif { if (viewport) { gf_mx2d_copy(backup, tr_state->transform); tr_state->traversing_mode = TRAVERSE_BINDABLE; tr_state->bounds = st->clip; gf_node_traverse(viewport, tr_state); tr_state->traversing_mode = TRAVERSE_PICK; group_2d_traverse(node, (GroupingNode2D *)st, tr_state); gf_mx2d_copy(tr_state->transform, backup); } else { group_2d_traverse(node, (GroupingNode2D *)st, tr_state); } } } break; case TRAVERSE_GET_BOUNDS: if (tr_state->for_node) { group_2d_traverse(node, (GroupingNode2D *)st, tr_state); } else { tr_state->bounds = st->clip; #ifndef GPAC_DISABLE_3D gf_bbox_from_rect(&tr_state->bbox, &st->clip); #endif } break; case TRAVERSE_DRAW_2D: group_2d_traverse(node, (GroupingNode2D *)st, tr_state); break; #ifndef GPAC_DISABLE_3D /*drawing a layer means drawing all sub-elements as a whole (no depth sorting with parents)*/ case TRAVERSE_DRAW_3D: assert(0); break; #endif } /*restore traversing state*/ tr_state->vp_size = prev_vp; tr_state->backgrounds = oldb; tr_state->viewpoints = oldv; tr_state->is_layer = prev_layer; #ifndef GPAC_DISABLE_3D tr_state->fogs = oldf; tr_state->navigations = oldn; #endif /*in case we missed bindables*/ if (st->first) { st->first = 0; gf_sc_invalidate(tr_state->visual->compositor, NULL); } }
static void svg_traverse_tspan(GF_Node *node, void *rs, Bool is_destroy) { SVGPropertiesPointers backup_props; u32 backup_flags; GF_Matrix2D backup_matrix; GF_Matrix mx3d; DrawableContext *ctx; SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVG_Element *tspan = (SVG_Element *)node; SVGAllAttributes atts; GF_ChildNodeItem *child; if (is_destroy) { drawable_del(st->drawable); svg_reset_text_stack(st); gf_list_del(st->spans); gf_free(st); return; } if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { svg_text_draw_2d(st, tr_state); return; } else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { gf_font_spans_get_selection(node, st->spans, tr_state); /*and browse children*/ child = ((GF_ParentNode *) tspan)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; } child = child->next; } return; } if (!tr_state->in_svg_text && !tr_state->in_svg_text_area) return; gf_svg_flatten_attributes(tspan, &atts); if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) return; if (tr_state->traversing_mode==TRAVERSE_PICK) { if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, GF_TRUE, st->drawable); /*and browse children*/ child = ((GF_ParentNode *) tspan)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: gf_node_traverse(child->node, tr_state); break; } child = child->next; } memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; return; } compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); if ( (st->prev_size != tr_state->svg_props->font_size->value) || (st->prev_flags != *tr_state->svg_props->font_style) || (st->prev_anchor != *tr_state->svg_props->text_anchor) || (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) ) ) { u32 mode; /*tspan has been modified in the SORT stage, which means that an anim local to tspan has modified the node. The result of the parent (text, textArea) will thus be wrong if we try to update the tspan. We therefore keep the previous computed drawable, and invalidate the parent for next frame*/ if (tr_state->traversing_mode==TRAVERSE_SORT) { gf_node_dirty_set(node, 0, GF_TRUE); goto skip_changes; } /*switch to bounds mode, and recompute children*/ mode = tr_state->traversing_mode; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; svg_reset_text_stack(st); child = ((GF_ParentNode *) tspan)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_DOMText: svg_traverse_domtext(child->node, &atts, tr_state, st->spans, NULL); break; case TAG_SVG_tspan: gf_node_dirty_set(child->node, 0, GF_FALSE); gf_node_traverse(child->node, tr_state); break; case TAG_SVG_switch: case TAG_SVG_a: case TAG_SVG_tbreak: gf_node_traverse(child->node, tr_state); break; default: break; } child = child->next; } tr_state->traversing_mode = mode; gf_node_dirty_clear(node, 0); drawable_mark_modified(st->drawable, tr_state); st->prev_size = tr_state->svg_props->font_size->value; st->prev_flags = *tr_state->svg_props->font_style; st->prev_anchor = *tr_state->svg_props->text_anchor; svg_update_bounds(st); } skip_changes: if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { if (tr_state->refresh_children_bounds) { if (tr_state->base_shift) svg_text_area_shift_bounds(st, tr_state); else svg_update_bounds(st); child = ((GF_ParentNode *) tspan)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: case TAG_SVG_switch: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; default: break; } child = child->next; } } if (!compositor_svg_is_display_off(tr_state->svg_props)) tr_state->bounds = st->bounds; } else if ( (tr_state->traversing_mode == TRAVERSE_SORT) && !compositor_svg_is_display_off(tr_state->svg_props) && ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { child = ((GF_ParentNode *) tspan)->children; ctx = drawable_init_context_svg(st->drawable, tr_state); if (ctx) svg_finalize_sort(ctx, st, tr_state); while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: case TAG_SVG_switch: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; default: break; } child = child->next; } } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
static void TraverseSound(GF_Node *node, void *rs, Bool is_destroy) { GF_TraverseState *tr_state = (GF_TraverseState*) rs; M_Sound *snd = (M_Sound *)node; SoundStack *st = (SoundStack *)gf_node_get_private(node); if (is_destroy) { gf_free(st); return; } if (!snd->source) return; tr_state->sound_holder = &st->snd_ifce; /*forward in case we're switched off*/ if (tr_state->switched_off) { gf_node_traverse((GF_Node *) snd->source, tr_state); } else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { /*we can't cull sound since*/ tr_state->disable_cull = 1; } else if (tr_state->traversing_mode==TRAVERSE_SORT) { GF_Matrix mx; SFVec3f usr, snd_dir, pos; Fixed mag, ang; /*this implies no DEF/USE for real location...*/ gf_mx_copy(st->mx, tr_state->model_matrix); gf_mx_copy(mx, tr_state->model_matrix); gf_mx_inverse(&mx); snd_dir = snd->direction; gf_vec_norm(&snd_dir); /*get user location*/ usr = tr_state->camera->position; gf_mx_apply_vec(&mx, &usr); /*recenter to ellipse focal*/ gf_vec_diff(usr, usr, snd->location); mag = gf_vec_len(usr); if (!mag) mag = FIX_ONE/10; ang = gf_divfix(gf_vec_dot(snd_dir, usr), mag); usr.z = gf_mulfix(ang, mag); usr.x = gf_sqrt(gf_mulfix(mag, mag) - gf_mulfix(usr.z, usr.z)); usr.y = 0; if (!gf_vec_equal(usr, st->last_pos)) { st->intensity = snd_compute_gain(snd->minBack, snd->minFront, snd->maxBack, snd->maxFront, usr); st->intensity = gf_mulfix(st->intensity, snd->intensity); st->last_pos = usr; } st->identity = (st->intensity==FIX_ONE) ? 1 : 0; if (snd->spatialize) { Fixed ang, sign; SFVec3f cross; pos = snd->location; gf_mx_apply_vec(&tr_state->model_matrix, &pos); gf_vec_diff(pos, pos, tr_state->camera->position); gf_vec_diff(usr, tr_state->camera->target, tr_state->camera->position); gf_vec_norm(&pos); gf_vec_norm(&usr); ang = gf_acos(gf_vec_dot(usr, pos)); /*get orientation*/ cross = gf_vec_cross(usr, pos); sign = gf_vec_dot(cross, tr_state->camera->up); if (sign>0) ang *= -1; ang = (FIX_ONE + gf_sin(ang)) / 2; st->lgain = (FIX_ONE - gf_mulfix(ang, ang)); st->rgain = FIX_ONE - gf_mulfix(FIX_ONE - ang, FIX_ONE - ang); /*renorm between 0 and 1*/ st->lgain = gf_mulfix(st->lgain, 4*st->intensity/3); st->rgain = gf_mulfix(st->rgain, 4*st->intensity/3); if (st->identity && ((st->lgain!=FIX_ONE) || (st->rgain!=FIX_ONE))) st->identity = 0; } else { st->lgain = st->rgain = FIX_ONE; } gf_node_traverse((GF_Node *) snd->source, tr_state); } tr_state->sound_holder = NULL; }
GF_Err visual_2d_init_draw(GF_VisualManager *visual, GF_TraverseState *tr_state) { GF_Err e; u32 rem, count; struct _drawable_store *it, *prev; #ifndef GPAC_DISABLE_VRML DrawableContext *ctx; M_Background2D *bck; #endif u32 draw_mode; /*reset display list*/ visual->cur_context = visual->context; if (visual->context) visual->context->drawable = NULL; visual->has_modif = 0; visual->has_overlays = 0; visual_2d_setup_projection(visual, tr_state); if (!visual->top_clipper.width || !visual->top_clipper.height) return GF_OK; tr_state->traversing_mode = TRAVERSE_SORT; visual->num_nodes_current_frame = 0; /*setup raster surface, brush and pen */ e = visual_2d_init_raster(visual); if (e) return e; draw_mode = 0; if (tr_state->immediate_draw) draw_mode = 1; /*if we're requested to invalidate everything, switch to direct drawing but don't reset bounds*/ else if (tr_state->invalidate_all) { tr_state->immediate_draw = 1; draw_mode = 2; } tr_state->invalidate_all = 0; /*reset prev nodes if any (previous traverse was indirect)*/ rem = count = 0; prev = NULL; it = visual->prev_nodes; while (it) { /*node was not drawn on this visual*/ if (!drawable_flush_bounds(it->drawable, visual, draw_mode)) { 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))); /*remove all bounds info related to this visual and unreg node */ drawable_reset_bounds(it->drawable, visual); if (prev) prev->next = it->next; else visual->prev_nodes = it->next; if (!it->next) visual->last_prev_entry = prev; rem++; gf_free(it); it = prev ? prev->next : visual->prev_nodes; } else { /*mark drawable as already registered with visual*/ it->drawable->flags |= DRAWABLE_REGISTERED_WITH_VISUAL; prev = it; it = it->next; count++; } } GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Top visual initialized - %d nodes registered and %d removed - using %s rendering\n", count, rem, draw_mode ? "direct" : "dirty-rect")); if (!draw_mode) return GF_OK; #ifndef GPAC_DISABLE_VRML /*direct mode, draw background*/ bck = (M_Background2D*) gf_list_get(visual->back_stack, 0); if (bck && bck->isBound) { ctx = b2d_get_context(bck, visual->back_stack); if (ctx) { /*force clearing entire zone, not just viewport, when using color. If texture, we MUST use the VP clipper in order to compute offsets when blitting bitmaps*/ if (ctx->aspect.fill_texture &&ctx->aspect.fill_texture->stream) { ctx->bi->clip = visual->top_clipper; } else { ctx->bi->clip = visual->surf_rect; } ctx->bi->unclip = gf_rect_ft(&ctx->bi->clip); tr_state->traversing_mode = TRAVERSE_BINDABLE; ctx->flags |= CTX_BACKROUND_NOT_LAYER; gf_node_traverse((GF_Node *) bck, tr_state); tr_state->traversing_mode = TRAVERSE_SORT; ctx->flags &= ~CTX_BACKROUND_NOT_LAYER; } else { visual->ClearSurface(visual, NULL, 0); } } else #endif { visual->ClearSurface(visual, NULL, 0); } return GF_OK; }
Bool visual_2d_terminate_draw(GF_VisualManager *visual, GF_TraverseState *tr_state) { u32 k, i, count, num_nodes, num_changed; GF_IRect refreshRect; Bool redraw_all; Bool hyb_force_redraw=GF_FALSE; #ifndef GPAC_DISABLE_VRML M_Background2D *bck = NULL; DrawableContext *bck_ctx = NULL; #endif DrawableContext *ctx; struct _drawable_store *it, *prev; DrawableContext *first_opaque = NULL; Bool has_clear = 0; Bool has_changed = 0; Bool redraw_all_on_background_change = GF_TRUE; /*in direct mode the visual is always redrawn*/ if (tr_state->immediate_draw) { /*flush pending contexts due to overlays*/ visual_2d_flush_overlay_areas(visual, tr_state); visual_2d_release_raster(visual); visual_clean_contexts(visual); visual->num_nodes_prev_frame = visual->num_nodes_current_frame; return 1; } num_changed = 0; /*if the aspect ratio has changed redraw everything*/ redraw_all = tr_state->invalidate_all; #ifndef GPAC_DISABLE_3D if (visual->compositor->hybrid_opengl && !visual->offscreen) redraw_all_on_background_change = GF_FALSE; #endif /*check for background changes for transparent nodes*/ #ifndef GPAC_DISABLE_VRML bck = (M_Background2D*)gf_list_get(visual->back_stack, 0); if (bck) { if (!bck->isBound) { if (visual->last_had_back) { if (redraw_all_on_background_change) redraw_all = 1; else hyb_force_redraw = 1; } visual->last_had_back = 0; } else { bck_ctx = b2d_get_context(bck, visual->back_stack); if (!visual->last_had_back || (bck_ctx->flags & CTX_REDRAW_MASK) ) { if (redraw_all_on_background_change) redraw_all = 1; else hyb_force_redraw = 1; } visual->last_had_back = (bck_ctx->aspect.fill_texture && !bck_ctx->aspect.fill_texture->transparent) ? 2 : 1; } } else #endif if (visual->last_had_back) { visual->last_had_back = 0; if (redraw_all_on_background_change) redraw_all = 1; else hyb_force_redraw = 1; } num_nodes = 0; ctx = visual->context; while (ctx && ctx->drawable) { num_nodes++; drawctx_update_info(ctx, visual); if (!redraw_all) { u32 res; assert( gf_irect_inside(&visual->top_clipper, &ctx->bi->clip) ); res = register_context_rect(&visual->to_redraw, ctx, num_nodes, &first_opaque); if (res) { num_changed ++; if (res==2) hyb_force_redraw=GF_TRUE; } } ctx = ctx->next; } /*garbage collection*/ /*clear all remaining bounds since last frames (the ones that moved or that are not drawn this frame)*/ prev = NULL; it = visual->prev_nodes; while (it) { while (drawable_get_previous_bound(it->drawable, &refreshRect, visual)) { if (!redraw_all) { //assert( gf_irect_inside(&visual->top_clipper, &refreshRect) ); gf_irect_intersect(&refreshRect, &visual->top_clipper); register_dirty_rect(&visual->to_redraw, &refreshRect); has_clear=1; } } /*if node is marked as undrawn, remove from visual*/ if (!(it->drawable->flags & DRAWABLE_DRAWN_ON_VISUAL)) { 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))); /*remove all bounds info related to this visual and unreg node */ drawable_reset_bounds(it->drawable, visual); it->drawable->flags &= ~DRAWABLE_REGISTERED_WITH_VISUAL; if (it->drawable->flags & DRAWABLE_IS_OVERLAY) { visual->compositor->video_out->Blit(visual->compositor->video_out, NULL, NULL, NULL, 1); } if (prev) prev->next = it->next; else visual->prev_nodes = it->next; if (!it->next) visual->last_prev_entry = prev; gf_free(it); it = prev ? prev->next : visual->prev_nodes; } else { prev = it; it = it->next; } } if (redraw_all) { ra_clear(&visual->to_redraw); ra_add(&visual->to_redraw, &visual->surf_rect); #ifdef TRACK_OPAQUE_REGIONS visual->to_redraw.list[0].opaque_node_index=0; #endif } else { ra_refresh(&visual->to_redraw); if (visual->compositor->debug_defer) { visual->ClearSurface(visual, &visual->top_clipper, 0); } } /*nothing to redraw*/ if (!hyb_force_redraw && ra_is_empty(&visual->to_redraw) ) { #ifndef GPAC_DISABLE_3D //force canvas draw visual->nb_objects_on_canvas_since_last_ogl_flush = 1; #endif GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] No changes found since last frame - skipping redraw\n")); goto exit; } has_changed = 1; tr_state->traversing_mode = TRAVERSE_DRAW_2D; if (first_opaque && (visual->to_redraw.count==1) && gf_rect_equal(first_opaque->bi->clip, visual->to_redraw.list[0].rect)) { visual->has_modif=0; goto skip_background; } /*redraw everything*/ #ifndef GPAC_DISABLE_VRML if (bck_ctx) { drawable_check_bounds(bck_ctx, visual); tr_state->ctx = bck_ctx; visual->draw_node_index = 0; /*force clearing entire zone, not just viewport, when using color. If texture, we MUST use the VP clipper in order to compute offsets when blitting bitmaps*/ if (bck_ctx->aspect.fill_texture && bck_ctx->aspect.fill_texture->stream) { bck_ctx->bi->clip = visual->top_clipper; } else { bck_ctx->bi->clip = visual->surf_rect; } bck_ctx->bi->unclip = gf_rect_ft(&bck_ctx->bi->clip); bck_ctx->next = visual->context; bck_ctx->flags |= CTX_BACKROUND_NOT_LAYER; gf_node_traverse(bck_ctx->drawable->node, tr_state); bck_ctx->flags &= ~CTX_BACKROUND_NOT_LAYER; } else #endif /*GPAC_DISABLE_VRML*/ { count = visual->to_redraw.count; for (k=0; k<count; k++) { GF_IRect rc; /*opaque area, skip*/ #ifdef TRACK_OPAQUE_REGIONS if (visual->to_redraw.list[k].opaque_node_index > 0) continue; #endif rc = visual->to_redraw.list[k].rect; visual->ClearSurface(visual, &rc, 0); } #ifndef GPAC_DISABLE_3D if (!count && hyb_force_redraw) { compositor_2d_hybgl_clear_surface_ex(tr_state->visual, NULL, 0, GF_FALSE); } #endif } if (!redraw_all && !has_clear) visual->has_modif=0; skip_background: #ifndef GPAC_DISABLE_LOG if (gf_log_tool_level_on(GF_LOG_COMPOSE, GF_LOG_INFO)) { GF_LOG(GF_LOG_INFO, 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)); if (visual->to_redraw.count>1) GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("\n")); for (i=0; i<visual->to_redraw.count; i++) { GF_LOG(GF_LOG_INFO, 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)); assert(visual->to_redraw.list[i].rect.width); } } #endif visual->draw_node_index = 0; ctx = visual->context; while (ctx && ctx->drawable) { visual->draw_node_index ++; tr_state->ctx = ctx; /*if overlay we cannot remove the context and cannot draw directly*/ if (! visual_2d_overlaps_overlay(tr_state->visual, ctx, tr_state)) { if (ctx->drawable->flags & DRAWABLE_USE_TRAVERSE_DRAW) { gf_node_traverse(ctx->drawable->node, tr_state); } else { drawable_draw(ctx->drawable, tr_state); } } ctx = ctx->next; } /*flush pending contexts due to overlays*/ visual_2d_flush_overlay_areas(visual, tr_state); #ifndef GPAC_DISABLE_VRML if (bck_ctx) bck_ctx->next = NULL; #endif if (visual->direct_flush) { GF_DirtyRectangles dr; u32 i; dr.count = visual->to_redraw.count; dr.list = gf_malloc(sizeof(GF_IRect)*dr.count); for (i=0; i<dr.count; i++) { dr.list[i] = visual->to_redraw.list[i].rect; } visual->compositor->video_out->FlushRectangles(visual->compositor->video_out, &dr); visual->compositor->skip_flush=1; gf_free(dr.list); } exit: /*clear dirty rects*/ ra_clear(&visual->to_redraw); visual_2d_release_raster(visual); visual_clean_contexts(visual); visual->num_nodes_prev_frame = visual->num_nodes_current_frame; return has_changed; }
static Bool composite_do_bindable(GF_Node *n, GF_TraverseState *tr_state, Bool force_check) { GF_Node *btop; Bool ret = 0; switch (gf_node_get_tag(n)) { #ifndef GPAC_DISABLE_3D case TAG_MPEG4_CompositeTexture3D: { M_CompositeTexture3D*c3d = (M_CompositeTexture3D*)n; if (force_check || gf_node_dirty_get(c3d->background)) { gf_node_traverse(c3d->background, tr_state); ret = 1; } btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); if (btop != c3d->background) { gf_node_unregister(c3d->background, n); gf_node_register(btop, n); c3d->background = btop; gf_node_event_out_str(n, "background"); ret = 1; } if (force_check || gf_node_dirty_get(c3d->viewpoint)) { gf_node_traverse(c3d->viewpoint, tr_state); ret = 1; } btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); if (btop != c3d->viewpoint) { gf_node_unregister(c3d->viewpoint, n); gf_node_register(btop, n); c3d->viewpoint = btop; gf_node_event_out_str(n, "viewpoint"); ret = 1; } if (force_check || gf_node_dirty_get(c3d->fog)) { gf_node_traverse(c3d->fog, tr_state); ret = 1; } btop = (GF_Node*)gf_list_get(tr_state->fogs, 0); if (btop != c3d->fog) { gf_node_unregister(c3d->fog, n); gf_node_register(btop, n); c3d->fog = btop; gf_node_event_out_str(n, "fog"); ret = 1; } if (force_check || gf_node_dirty_get(c3d->navigationInfo)) { gf_node_traverse(c3d->navigationInfo, tr_state); ret = 1; } btop = (GF_Node*)gf_list_get(tr_state->navigations, 0); if (btop != c3d->navigationInfo) { gf_node_unregister(c3d->navigationInfo, n); gf_node_register(btop, n); c3d->navigationInfo = btop; gf_node_event_out_str(n, "navigationInfo"); ret = 1; } return ret; } #endif case TAG_MPEG4_CompositeTexture2D: { M_CompositeTexture2D *c2d = (M_CompositeTexture2D*)n; if (force_check || gf_node_dirty_get(c2d->background)) { gf_node_traverse(c2d->background, tr_state); ret = 1; } btop = (GF_Node*)gf_list_get(tr_state->backgrounds, 0); if (btop != c2d->background) { gf_node_unregister(c2d->background, n); gf_node_register(btop, n); c2d->background = btop; gf_node_event_out_str(n, "background"); ret = 1; } if (force_check || gf_node_dirty_get(c2d->viewport)) { gf_node_traverse(c2d->viewport, tr_state); ret = 1; } btop = (GF_Node*)gf_list_get(tr_state->viewpoints, 0); if (btop != c2d->viewport) { gf_node_unregister(c2d->viewport, n); gf_node_register(btop, n); c2d->viewport = btop; gf_node_event_out_str(n, "viewport"); ret = 1; } return ret; } } return 0; }
/* Notifies the scene time to a timed element, potentially changing its status and triggering its evaluation Returns: 0 if no rendering traversal is required, 1 if a rendering traversal is required, -1 if the time node is a discard which has been deleted during this notification, -2 means that the timed element is waiting to begin, -3 means that the timed element is active but does not need further notifications (set without dur) but still requires a rendering traversal */ s32 gf_smil_timing_notify_time(SMIL_Timing_RTI *rti, Double in_scene_time) { s32 ret = 0; GF_DOM_Event evt; SMILTimingAttributesPointers *timingp = rti->timingp; Bool force_end = 0; if (!timingp) return 0; /* if the scene time is the same as it was during the previous notification, it means that the animations are paused and we don't need to evaluate it again unless the force_reevaluation flag is set */ if ((rti->scene_time == in_scene_time) && (rti->force_reevaluation == 0)) return 0; if (!rti->paused) rti->scene_time = in_scene_time; rti->force_reevaluation = 0; /* for fraction events, in all cases we indicate that the scene needs redraw */ if (rti->evaluate_status == SMIL_TIMING_EVAL_FRACTION) return 1; if (rti->evaluate_status == SMIL_TIMING_EVAL_DISCARD) { /* TODO: FIX ME discarding should send a begin event ? */ /* Since the discard can only be evaluated once, it unregisters itself from the list of timed elements to be notified, so for this special case we return -1 when the discard has actually been executed */ if (gf_smil_discard(rti, FLT2FIX(rti->scene_time))) return -1; else return 0; } gf_node_register(rti->timed_elt, NULL); waiting_to_begin: if (rti->status == SMIL_STATUS_WAITING_TO_BEGIN) { if (rti->current_interval->begin != -1 && rti->scene_time >= rti->current_interval->begin) { /* if there is a computed interval with a definite begin value and if that value is lesser than the scene time, then the animation becomes active */ GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Activating\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); rti->status = SMIL_STATUS_ACTIVE; if (rti->timed_elt->sgprivate->tag==TAG_LSR_conditional) { SVG_Element *e = (SVG_Element *)rti->timed_elt; /*activate conditional*/ if (e->children) gf_node_traverse(e->children->node, NULL); rti->status = SMIL_STATUS_DONE; } else { gf_smil_reorder_anim(rti); } memset(&evt, 0, sizeof(evt)); evt.type = GF_EVENT_BEGIN_EVENT; evt.smil_event_time = rti->current_interval->begin; gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt); } else { GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Evaluating (Not starting)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); ret = -2; goto exit; } } if (rti->status == SMIL_STATUS_ACTIVE) { u32 cur_id; if (rti->current_interval->active_duration >= 0 && rti->scene_time >= (rti->current_interval->begin + rti->current_interval->active_duration)) { force_end: GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Stopping \n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, NULL); ret = rti->postpone; if (timingp->fill && *timingp->fill == SMIL_FILL_FREEZE) { rti->status = SMIL_STATUS_FROZEN; rti->evaluate_status = SMIL_TIMING_EVAL_FREEZE; GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Preparing to freeze\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); if (!rti->postpone) { rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); } } else { rti->status = SMIL_STATUS_DONE; rti->evaluate_status = SMIL_TIMING_EVAL_REMOVE; GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Preparing to remove\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); if (!rti->postpone) { rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); } } memset(&evt, 0, sizeof(evt)); evt.type = GF_EVENT_END_EVENT; /* WARNING: begin + active_duration may be greater than 'now' because of force_end cases */ evt.smil_event_time = rti->current_interval->begin + rti->current_interval->active_duration; gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt); } else { /* the animation is still active */ if (!timingp->restart || *timingp->restart == SMIL_RESTART_ALWAYS) { GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Checking for restart (always)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); if (rti->next_interval->begin != -1 && rti->next_interval->begin < rti->scene_time) { *rti->current_interval = *rti->next_interval; gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->scene_time); /* mark that this element has been modified and need to be reinserted at its proper place in the list of timed elements in the scenegraph */ gf_smil_mark_modified(rti, 0); /* if this is animation, reinserting the animation in the list of animations that targets this attribute, so that it is the last one */ gf_smil_reorder_anim(rti); memset(&evt, 0, sizeof(evt)); evt.type = GF_EVENT_BEGIN_EVENT; evt.smil_event_time = rti->current_interval->begin; gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt); } } ret = rti->postpone; cur_id = rti->current_interval->nb_iterations; rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, &force_end); if (force_end) { GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Forcing end (fill or remove)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); goto force_end; } if (cur_id < rti->current_interval->nb_iterations) { GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[SMIL Timing ] Time %f - Timed element %s - Preparing to repeat\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); memset(&evt, 0, sizeof(evt)); evt.type = GF_EVENT_REPEAT_EVENT; evt.smil_event_time = rti->current_interval->begin + rti->current_interval->nb_iterations*rti->current_interval->simple_duration; evt.detail = rti->current_interval->nb_iterations; gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt); rti->evaluate_status = SMIL_TIMING_EVAL_REPEAT; } else { GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Preparing to update\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); rti->evaluate_status = SMIL_TIMING_EVAL_UPDATE; } if (!rti->postpone) { rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status); } /* special case for animations with unspecified simpleDur (not with media timed elements) we need to indicate that this anim does not need to be notified anymore and that it does not require tree traversal */ if (gf_svg_is_animation_tag(rti->timed_elt->sgprivate->tag) && (rti->current_interval->simple_duration==-1) && (rti->current_interval->active_duration==-1) ) { /*GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph; while (sg->parent_scene) sg = sg->parent_scene; gf_list_del_item(sg->smil_timed_elements, rti); ret = -3;*/ ret = 1; } } } if ((rti->status == SMIL_STATUS_DONE) || (rti->status == SMIL_STATUS_FROZEN)) { if (!timingp->restart || *timingp->restart != SMIL_RESTART_NEVER) { /* Check changes in begin or end attributes */ GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Checking for restart when not active\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); if (rti->next_interval->begin != -1) { Bool restart_timing = 0; /*next interval is right now*/ if (rti->next_interval->begin == rti->current_interval->begin+rti->current_interval->active_duration) restart_timing = 1; /*switch intervals*/ if (rti->next_interval->begin >= rti->current_interval->begin+rti->current_interval->active_duration) { *rti->current_interval = *rti->next_interval; gf_smil_timing_print_interval(rti, 1, rti->current_interval); gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->scene_time); /* mark that this element has been modified and need to be reinserted at its proper place in the list of timed elements in the scenegraph */ gf_smil_mark_modified(rti, 0); } else { rti->next_interval->begin = -1; } /*if chaining to new interval, go to wait_for begin right now*/ if (restart_timing) { rti->status = SMIL_STATUS_WAITING_TO_BEGIN; rti->evaluate_status = SMIL_TIMING_EVAL_NONE; GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing ] Time %f - Timed element %s - Returning to eval none status\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt))); ret = 0; goto waiting_to_begin; } /*otherwise move state to waiting for begin for next smil_timing evaluation, but don't change evaluate status for next anim evaluation*/ else { rti->status = SMIL_STATUS_WAITING_TO_BEGIN; } } else { /*??? what is this ???*/ //ret = 0; } } else if ((rti->status == SMIL_STATUS_DONE) && timingp->restart && (*timingp->restart == SMIL_RESTART_NEVER)) { /* the timed element is done and cannot restart, we don't need to evaluate it anymore */ GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph; while (sg->parent_scene) sg = sg->parent_scene; gf_list_del_item(sg->smil_timed_elements, rti); ret = -1; } } exit: gf_node_unregister(rti->timed_elt, NULL); return ret; }
Bool gf_sc_fit_world_to_screen(GF_Compositor *compositor) { GF_TraverseState tr_state; SFVec3f pos, diff; Fixed dist, d; GF_Camera *cam; GF_Node *top; #ifndef GPAC_DISABLE_VRML // if (gf_list_count(compositor->visual->back_stack)) return; if (gf_list_count(compositor->visual->view_stack)) return 0; #endif gf_mx_p(compositor->mx); top = gf_sg_get_root_node(compositor->scene); if (!top) { gf_mx_v(compositor->mx); return 0; } memset(&tr_state, 0, sizeof(GF_TraverseState)); gf_mx_init(tr_state.model_matrix); tr_state.traversing_mode = TRAVERSE_GET_BOUNDS; tr_state.visual = compositor->visual; gf_node_traverse(top, &tr_state); if (gf_node_dirty_get(top)) { tr_state.bbox.is_set = 0; } if (!tr_state.bbox.is_set) { gf_mx_v(compositor->mx); /*empty world ...*/ if (tr_state.bbox.radius==-1) return 1; /*2D world with 3D camera forced*/ if (tr_state.bounds.width&&tr_state.bounds.height) return 1; return 0; } cam = &compositor->visual->camera; cam->world_bbox = tr_state.bbox; /*fit is based on bounding sphere*/ dist = gf_divfix(tr_state.bbox.radius, gf_sin(cam->fieldOfView/2) ); gf_vec_diff(diff, cam->center, tr_state.bbox.center); /*do not update if camera is outside the scene bounding sphere and dist is too close*/ if (gf_vec_len(diff) > tr_state.bbox.radius + cam->radius) { gf_vec_diff(diff, cam->vp_position, tr_state.bbox.center); d = gf_vec_len(diff); if (d<dist) { gf_mx_v(compositor->mx); return 1; } } diff = gf_vec_scale(camera_get_pos_dir(cam), dist); gf_vec_add(pos, tr_state.bbox.center, diff); diff = cam->position; camera_set_vectors(cam, pos, cam->vp_orientation, cam->fieldOfView); cam->position = diff; camera_move_to(cam, pos, cam->target, cam->up); cam->examine_center = tr_state.bbox.center; cam->flags |= CF_STORE_VP; if (cam->z_far < dist) cam->z_far = 10*dist; camera_changed(compositor, cam); gf_mx_v(compositor->mx); return 1; }
static void TraversePlanarExtrusion(GF_Node *node, void *rs, Bool is_destroy) { PlanarExtrusion plane_ext; Drawable *stack_2d; u32 i, j, k; MFVec3f spine_vec; SFVec3f d; Fixed spine_len; GF_Rect bounds; GF_Path *geo, *spine; GF_TraverseState *tr_state = (GF_TraverseState *)rs; Drawable3D *stack = (Drawable3D *)gf_node_get_private(node); if (is_destroy) { drawable_3d_del(node); return; } if (!PlanarExtrusion_GetNode(node, &plane_ext)) return; if (!plane_ext.geometry || !plane_ext.spine) return; if (gf_node_dirty_get(node)) { u32 cur, nb_pts; u32 mode = tr_state->traversing_mode; geo = spine = NULL; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; gf_node_traverse(plane_ext.geometry, tr_state); gf_node_traverse(plane_ext.spine, tr_state); tr_state->traversing_mode = mode; switch (gf_node_get_tag(plane_ext.geometry) ) { case TAG_MPEG4_Circle: case TAG_MPEG4_Ellipse: case TAG_MPEG4_Rectangle: case TAG_MPEG4_Curve2D: case TAG_MPEG4_XCurve2D: case TAG_MPEG4_IndexedFaceSet2D: case TAG_MPEG4_IndexedLineSet2D: stack_2d = (Drawable*)gf_node_get_private(plane_ext.geometry); if (stack_2d) geo = stack_2d->path; break; default: return; } switch (gf_node_get_tag(plane_ext.spine) ) { case TAG_MPEG4_Circle: case TAG_MPEG4_Ellipse: case TAG_MPEG4_Rectangle: case TAG_MPEG4_Curve2D: case TAG_MPEG4_XCurve2D: case TAG_MPEG4_IndexedFaceSet2D: case TAG_MPEG4_IndexedLineSet2D: stack_2d = (Drawable*)gf_node_get_private(plane_ext.spine); if (stack_2d) spine = stack_2d->path; break; default: return; } if (!geo || !spine) return; mesh_reset(stack->mesh); gf_path_flatten(spine); gf_path_get_bounds(spine, &bounds); gf_path_flatten(geo); gf_path_get_bounds(geo, &bounds); cur = 0; for (i=0; i<spine->n_contours; i++) { nb_pts = 1 + spine->contours[i] - cur; spine_vec.vals = NULL; gf_sg_vrml_mf_alloc(&spine_vec, GF_SG_VRML_MFVEC3F, nb_pts); spine_len = 0; for (j=cur; j<nb_pts; j++) { spine_vec.vals[j].x = spine->points[j].x; spine_vec.vals[j].y = spine->points[j].y; spine_vec.vals[j].z = 0; if (j) { gf_vec_diff(d, spine_vec.vals[j], spine_vec.vals[j-1]); spine_len += gf_vec_len(d); } } cur += nb_pts; if (!plane_ext.orientation->count && !plane_ext.scale->count) { mesh_extrude_path_ext(stack->mesh, geo, &spine_vec, plane_ext.creaseAngle, bounds.x, bounds.y-bounds.height, bounds.width, bounds.height, plane_ext.beginCap, plane_ext.endCap, NULL, NULL, plane_ext.txAlongSpine); } /*interpolate orientation and scale along subpath line*/ else { MFRotation ori; MFVec2f scale; Fixed cur_len, frac; ori.vals = NULL; gf_sg_vrml_mf_alloc(&ori, GF_SG_VRML_MFROTATION, nb_pts); scale.vals = NULL; gf_sg_vrml_mf_alloc(&scale, GF_SG_VRML_MFVEC2F, nb_pts); cur_len = 0; if (!plane_ext.orientation->count) ori.vals[0].y = FIX_ONE; if (!plane_ext.scale->count) scale.vals[0].x = scale.vals[0].y = FIX_ONE; for (j=0; j<nb_pts; j++) { if (j) { gf_vec_diff(d, spine_vec.vals[j], spine_vec.vals[j-1]); cur_len += gf_vec_len(d); ori.vals[j] = ori.vals[j-1]; scale.vals[j] = scale.vals[j-1]; } if (plane_ext.orientation->count && (plane_ext.orientation->count == plane_ext.orientationKeys->count)) { frac = gf_divfix(cur_len , spine_len); if (frac < plane_ext.orientationKeys->vals[0]) ori.vals[j] = plane_ext.orientation->vals[0]; else if (frac >= plane_ext.orientationKeys->vals[plane_ext.orientationKeys->count-1]) ori.vals[j] = plane_ext.orientation->vals[plane_ext.orientationKeys->count-1]; else { for (k=1; k<plane_ext.orientationKeys->count; k++) { Fixed kDiff = plane_ext.orientationKeys->vals[k] - plane_ext.orientationKeys->vals[k-1]; if (!kDiff) continue; if (frac < plane_ext.orientationKeys->vals[k-1]) continue; if (frac > plane_ext.orientationKeys->vals[k]) continue; frac = gf_divfix(frac - plane_ext.orientationKeys->vals[k-1], kDiff); break; } ori.vals[j] = gf_sg_sfrotation_interpolate(plane_ext.orientation->vals[k-1], plane_ext.orientation->vals[k], frac); } } if (plane_ext.scale->count == plane_ext.scaleKeys->count) { frac = gf_divfix(cur_len , spine_len); if (frac <= plane_ext.scaleKeys->vals[0]) scale.vals[j] = plane_ext.scale->vals[0]; else if (frac >= plane_ext.scaleKeys->vals[plane_ext.scaleKeys->count-1]) scale.vals[j] = plane_ext.scale->vals[plane_ext.scale->count-1]; else { for (k=1; k<plane_ext.scaleKeys->count; k++) { Fixed kDiff = plane_ext.scaleKeys->vals[k] - plane_ext.scaleKeys->vals[k-1]; if (!kDiff) continue; if (frac < plane_ext.scaleKeys->vals[k-1]) continue; if (frac > plane_ext.scaleKeys->vals[k]) continue; frac = gf_divfix(frac - plane_ext.scaleKeys->vals[k-1], kDiff); break; } scale.vals[j].x = gf_mulfix(plane_ext.scale->vals[k].x - plane_ext.scale->vals[k-1].x, frac) + plane_ext.scale->vals[k-1].x; scale.vals[j].y = gf_mulfix(plane_ext.scale->vals[k].y - plane_ext.scale->vals[k-1].y, frac) + plane_ext.scale->vals[k-1].y; } } } mesh_extrude_path_ext(stack->mesh, geo, &spine_vec, plane_ext.creaseAngle, bounds.x, bounds.y-bounds.height, bounds.width, bounds.height, plane_ext.beginCap, plane_ext.endCap, &ori, &scale, plane_ext.txAlongSpine); gf_sg_vrml_mf_reset(&ori, GF_SG_VRML_MFROTATION); gf_sg_vrml_mf_reset(&scale, GF_SG_VRML_MFVEC2F); } gf_sg_vrml_mf_reset(&spine_vec, GF_SG_VRML_MFVEC3F); } mesh_update_bounds(stack->mesh); gf_mesh_build_aabbtree(stack->mesh); } if (tr_state->traversing_mode==TRAVERSE_DRAW_3D) { visual_3d_draw(tr_state, stack->mesh); } else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { tr_state->bbox = stack->mesh->bounds; } }
static void svg_traverse_textArea(GF_Node *node, void *rs, Bool is_destroy) { SVGPropertiesPointers backup_props; u32 backup_flags; GF_Matrix mx3d; GF_Matrix2D backup_matrix; DrawableContext *ctx = NULL; GF_ChildNodeItem *child; SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVG_Element *text = (SVG_Element *)node; SVGAllAttributes atts; if (is_destroy) { drawable_del(st->drawable); svg_reset_text_stack(st); gf_list_del(st->spans); gf_free(st); return; } if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) { svg_text_draw_2d(st, tr_state); return; } else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) { tr_state->text_parent = node; gf_font_spans_get_selection(node, st->spans, tr_state); /*and browse children*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_SVG_tspan: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; } child = child->next; } tr_state->text_parent = NULL; return; } gf_svg_flatten_attributes(text, &atts); if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags)) return; tr_state->text_parent = node; tr_state->in_svg_text_area++; if (tr_state->traversing_mode==TRAVERSE_PICK) { if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) { compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, GF_TRUE, st->drawable); /*and browse children*/ child = ((GF_ParentNode *) node)->children; while (child) { gf_node_traverse(child->node, tr_state); child = child->next; } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; } tr_state->in_svg_text_area--; tr_state->text_parent = NULL; return; } compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d); if ( (st->prev_size != tr_state->svg_props->font_size->value) || (st->prev_flags != *tr_state->svg_props->font_style) || (st->prev_anchor != *tr_state->svg_props->text_anchor) || (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) ) || tr_state->visual->compositor->reset_fonts ) { u32 mode; child = ((GF_ParentNode *) text)->children; svg_reset_text_stack(st); gf_node_dirty_clear(node, 0); drawable_mark_modified(st->drawable, tr_state); drawable_reset_path(st->drawable); tr_state->max_length = (atts.width ? (atts.width->type == SVG_NUMBER_AUTO ? FIX_MAX : atts.width->value) : FIX_MAX); tr_state->max_height = (atts.height ? (atts.height->type == SVG_NUMBER_AUTO ? FIX_MAX : atts.height->value) : FIX_MAX); tr_state->base_x = (atts.x ? atts.x->value : 0); tr_state->base_y = (atts.y ? atts.y->value : 0); /*init the xml:space algo*/ tr_state->last_char_type = 0; /*let it initialize from first font*/ tr_state->line_spacing = 0; tr_state->text_end_x = 0; tr_state->text_end_y = (tr_state->svg_props->line_increment->type == SVG_NUMBER_AUTO ? 0 : tr_state->svg_props->line_increment->value); tr_state->x_anchors = gf_list_new(); if (tr_state->svg_props->font_size && (tr_state->svg_props->font_size->value <= tr_state->max_height)) { Fixed remain; u32 c, refresh_to_idx, prev_refresh; /*switch to bounds mode, and recompute children*/ mode = tr_state->traversing_mode; tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; prev_refresh = tr_state->refresh_children_bounds; tr_state->refresh_children_bounds = 0; c = refresh_to_idx = 0; child = ((GF_ParentNode *) text)->children; while (child) { c++; switch (gf_node_get_tag(child->node)) { case TAG_DOMText: svg_traverse_dom_text_area(child->node, &atts, tr_state, st->spans); break; case TAG_SVG_tspan: /*mark tspan as dirty to force rebuild*/ gf_node_dirty_set(child->node, 0, GF_FALSE); gf_node_traverse(child->node, tr_state); break; case TAG_SVG_switch: case TAG_SVG_a: case TAG_SVG_tbreak: gf_node_traverse(child->node, tr_state); break; default: break; } if (tr_state->refresh_children_bounds) { tr_state->refresh_children_bounds=0; refresh_to_idx=c; } child=child->next; } st->prev_size = tr_state->svg_props->font_size->value; st->prev_flags = *tr_state->svg_props->font_style; st->prev_anchor = *tr_state->svg_props->text_anchor; svg_text_area_reset_state(tr_state); gf_list_del(tr_state->x_anchors); tr_state->x_anchors = NULL; remain = 0; if (tr_state->refresh_children_bounds) { refresh_to_idx = (u32) -1; tr_state->base_shift = 0; } if (tr_state->svg_props->display_align) { switch (*tr_state->svg_props->display_align) { case SVG_DISPLAYALIGN_CENTER: remain = (tr_state->max_height-tr_state->text_end_y) / 2; break; case SVG_DISPLAYALIGN_AFTER: remain = tr_state->max_height - tr_state->text_end_y; break; default: remain = 0; break; } if (remain<0) remain=0; if (remain) { refresh_to_idx = (u32) -1; tr_state->base_shift = remain; svg_text_area_shift_bounds(st, tr_state); } } if (refresh_to_idx) { tr_state->refresh_children_bounds=1; /*and retraverse in case of bounds adjustements*/ child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_DOMText: break; case TAG_SVG_tspan: case TAG_SVG_switch: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; default: break; } child=child->next; refresh_to_idx--; if (!refresh_to_idx) break; } tr_state->base_shift = 0; } tr_state->traversing_mode = mode; tr_state->refresh_children_bounds = prev_refresh; } svg_update_bounds(st); } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { if (!compositor_svg_is_display_off(tr_state->svg_props)) tr_state->bounds = st->bounds; } else if ( (tr_state->traversing_mode == TRAVERSE_SORT) && !compositor_svg_is_display_off(tr_state->svg_props) && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { ctx = drawable_init_context_svg(st->drawable, tr_state); if (ctx) svg_finalize_sort(ctx, st, tr_state); child = ((GF_ParentNode *) text)->children; while (child) { switch (gf_node_get_tag(child->node)) { case TAG_DOMText: break; case TAG_SVG_tspan: case TAG_SVG_switch: case TAG_SVG_a: gf_node_traverse(child->node, tr_state); break; default: break; } child = child->next; } } tr_state->in_svg_text_area--; tr_state->text_parent = NULL; compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d); memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }