void compositor_svg_apply_local_transformation(GF_TraverseState *tr_state, SVGAllAttributes *atts, GF_Matrix2D *backup_matrix_2d, GF_Matrix *backup_matrix) { #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d && backup_matrix) { GF_Matrix tmp; Bool is_draw = (tr_state->traversing_mode==TRAVERSE_SORT) ? 1 : 0; gf_mx_copy(*backup_matrix, tr_state->model_matrix); if (is_draw) visual_3d_matrix_push(tr_state->visual); if (atts->transform && atts->transform->is_ref) { gf_mx_from_mx2d(&tr_state->model_matrix, &tr_state->vb_transform); if (is_draw) { GF_Matrix tmp; gf_mx_init(tmp); gf_mx_add_translation(&tmp, -tr_state->camera->width/2, tr_state->camera->height/2, 0); gf_mx_add_scale(&tmp, FIX_ONE, -FIX_ONE, FIX_ONE); gf_mx_add_matrix(&tmp, &tr_state->model_matrix); visual_3d_matrix_load(tr_state->visual, tmp.m); } } if (atts->motionTransform) { if (is_draw) { gf_mx_from_mx2d(&tmp, atts->motionTransform); visual_3d_matrix_add(tr_state->visual, tmp.m); } else { gf_mx_add_matrix_2d(&tr_state->model_matrix, atts->motionTransform); } } if (atts->transform) { if (is_draw) { gf_mx_from_mx2d(&tmp, &atts->transform->mat); visual_3d_matrix_add(tr_state->visual, tmp.m); } else { gf_mx_add_matrix_2d(&tr_state->model_matrix, &atts->transform->mat); } } return; } #endif gf_mx2d_copy(*backup_matrix_2d, tr_state->transform); if (atts->transform && atts->transform->is_ref) gf_mx2d_copy(tr_state->transform, tr_state->vb_transform); if (atts->motionTransform) gf_mx2d_pre_multiply(&tr_state->transform, atts->motionTransform); if (atts->transform) gf_mx2d_pre_multiply(&tr_state->transform, &atts->transform->mat); }
/* This is the generic routine for child traversing - note we are not duplicating the effect */ void gf_svg_sani_apply_local_transformation(RenderEffect2D *eff, GF_Node *node, GF_Matrix2D *backup_matrix) { gf_mx2d_copy(*backup_matrix, eff->transform); if (((SVG_SANI_TransformableElement *)node)->transform.is_ref) gf_mx2d_copy(eff->transform, eff->vb_transform); if (((SVG_SANI_TransformableElement *)node)->motionTransform) gf_mx2d_pre_multiply(&eff->transform, ((SVG_SANI_TransformableElement *)node)->motionTransform); gf_mx2d_pre_multiply(&eff->transform, &((SVG_SANI_TransformableElement *)node)->transform.mat); }
static void traverse_transform(GF_Node *node, Transform2DStack *stack, GF_TraverseState *tr_state) { if (stack->is_null) return; /*note we don't clear dirty flag, this is done in traversing*/ if (stack->is_identity) { group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); } #ifndef GPAC_DISABLE_3D else if (tr_state->visual->type_3d) { GF_Matrix mx_bckup; gf_mx_copy(mx_bckup, tr_state->model_matrix); gf_mx_add_matrix_2d(&tr_state->model_matrix, &stack->mat); group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); gf_mx_copy(tr_state->model_matrix, mx_bckup); } #endif else { GF_Matrix2D bckup; gf_mx2d_copy(bckup, tr_state->transform); gf_mx2d_pre_multiply(&tr_state->transform, &stack->mat); group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); gf_mx2d_copy(tr_state->transform, bckup); } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { gf_mx2d_apply_rect(&stack->mat, &tr_state->bounds); } }
/* Node specific rendering functions All the nodes follow the same principles: * Check if the display property is not set to none, otherwise do not render * Back-up of the coordinate system & apply geometric transformation if any * Render the children if any or the shape if leaf node * Restore coordinate system */ static void svg_sani_render_svg(GF_Node *node, void *rs, Bool is_destroy) { u32 viewport_color; GF_Matrix2D backup_matrix; GF_IRect top_clip; Bool is_root_svg = 1; SVG_SANI_svgElement *svg = (SVG_SANI_svgElement *)node; RenderEffect2D *eff = (RenderEffect2D *) rs; if (is_destroy) return; /*enable or disable navigation*/ eff->surface->render->navigation_disabled = (svg->zoomAndPan == SVG_ZOOMANDPAN_DISABLE) ? 1 : 0; svg_sani_render_base(node, eff); if (svg->display == SVG_DISPLAY_NONE) return; top_clip = eff->surface->top_clipper; gf_mx2d_copy(backup_matrix, eff->transform); gf_mx2d_init(eff->vb_transform); gf_svg_sani_set_viewport_transformation(eff, svg, is_root_svg); gf_mx2d_pre_multiply(&eff->transform, &eff->vb_transform); if (!is_root_svg && (svg->x.value || svg->y.value)) gf_mx2d_add_translation(&eff->transform, svg->x.value, svg->y.value); /* TODO: FIX ME: this only works for single SVG element in the doc*/ if (is_root_svg && svg->viewport_fill.type != SVG_PAINT_NONE) { viewport_color = GF_COL_ARGB_FIXED(svg->viewport_fill_opacity.value, svg->viewport_fill.color.red, svg->viewport_fill.color.green, svg->viewport_fill.color.blue); if (eff->surface->render->compositor->back_color != viewport_color) { eff->invalidate_all = 1; eff->surface->render->compositor->back_color = viewport_color; } } if (eff->traversing_mode == TRAVERSE_GET_BOUNDS) { svg_get_nodes_bounds(node, svg->children, eff); } else { svg_render_node_list(svg->children, eff); } gf_svg_sani_restore_parent_transformation(eff, &backup_matrix); eff->surface->top_clipper = top_clip; }
void r2d_render_svg_sani_use(GF_Node *node, GF_Node *sub_root, void *rs) { GF_Matrix2D backup_matrix; GF_Matrix2D translate; GF_Node *prev_use; SVG_SANI_useElement *use = (SVG_SANI_useElement *)node; RenderEffect2D *eff = (RenderEffect2D *)rs; svg_sani_render_base(node, eff); gf_mx2d_init(translate); translate.m[2] = use->x.value; translate.m[5] = use->y.value; if (eff->traversing_mode == TRAVERSE_GET_BOUNDS) { gf_svg_sani_apply_local_transformation(eff, node, &backup_matrix); if (use->display != SVG_DISPLAY_NONE) { gf_node_render((GF_Node*)use->xlink->href.target, eff); gf_mx2d_apply_rect(&translate, &eff->bounds); } gf_svg_sani_restore_parent_transformation(eff, &backup_matrix); return; } if (use->display == SVG_DISPLAY_NONE || use->visibility == SVG_VISIBILITY_HIDDEN) { return; } gf_svg_sani_apply_local_transformation(eff, node, &backup_matrix); gf_mx2d_pre_multiply(&eff->transform, &translate); prev_use = eff->parent_use; eff->parent_use = use->xlink->href.target; gf_node_render(use->xlink->href.target, eff); eff->parent_use = prev_use; gf_svg_sani_restore_parent_transformation(eff, &backup_matrix); }
static void TraverseViewport(GF_Node *node, void *rs, Bool is_destroy) { Fixed sx, sy, w, h, tx, ty; #ifndef GPAC_DISABLE_3D GF_Matrix mx; #endif GF_Matrix2D mat; GF_Rect rc, rc_bckup; ViewStack *st = (ViewStack *) gf_node_get_private(node); M_Viewport *vp = (M_Viewport *) node; GF_TraverseState *tr_state = (GF_TraverseState *)rs; if (is_destroy) { DestroyViewStack(node); return; } #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d>1) return; #endif /*first traverse, bound if needed*/ if (gf_list_find(tr_state->viewpoints, node) < 0) { gf_list_add(tr_state->viewpoints, node); assert(gf_list_find(st->reg_stacks, tr_state->viewpoints)==-1); gf_list_add(st->reg_stacks, tr_state->viewpoints); if (gf_list_get(tr_state->viewpoints, 0) == vp) { if (!vp->isBound) Bindable_SetIsBound(node, 1); } else { if (gf_inline_is_default_viewpoint(node)) Bindable_SetSetBindEx(node, 1, tr_state->viewpoints); } VPCHANGED(tr_state->visual->compositor); /*in any case don't draw the first time (since the viewport could have been declared last)*/ gf_sc_invalidate(tr_state->visual->compositor, NULL); return; } if (tr_state->traversing_mode != TRAVERSE_BINDABLE) return; if (!vp->isBound) return; if (gf_list_get(tr_state->viewpoints, 0) != vp) return; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { w = tr_state->bbox.max_edge.x - tr_state->bbox.min_edge.x; h = tr_state->bbox.max_edge.y - tr_state->bbox.min_edge.y; } else #endif { w = tr_state->bounds.width; h = tr_state->bounds.height; } if (!w || !h) return; /*if no parent this is the main viewport, don't update if not changed*/ // if (!tr_state->is_layer && !gf_node_dirty_get(node)) return; gf_node_dirty_clear(node, 0); gf_mx2d_init(mat); gf_mx2d_add_translation(&mat, vp->position.x, vp->position.y); gf_mx2d_add_rotation(&mat, 0, 0, vp->orientation); //compute scaling ratio sx = (vp->size.x>=0) ? vp->size.x : w; sy = (vp->size.y>=0) ? vp->size.y : h; rc = gf_rect_center(sx, sy); rc_bckup = rc; switch (vp->fit) { /*covers all area and respect aspect ratio*/ case 2: if (gf_divfix(rc.width, w) > gf_divfix(rc.height, h)) { rc.width = gf_muldiv(rc.width, h, rc.height); rc.height = h; } else { rc.height = gf_muldiv(rc.height , w, rc.width); rc.width = w; } break; /*fits inside the area and respect AR*/ case 1: if (gf_divfix(rc.width, w)> gf_divfix(rc.height, h)) { rc.height = gf_muldiv(rc.height, w, rc.width); rc.width = w; } else { rc.width = gf_muldiv(rc.width, h, rc.height); rc.height = h; } break; /*fit entirely: nothing to change*/ case 0: rc.width = w; rc.height = h; break; default: return; } sx = gf_divfix(rc.width, rc_bckup.width); sy = gf_divfix(rc.height, rc_bckup.height); /*viewport on root visual, remove compositor scale*/ if (!tr_state->is_layer && (tr_state->visual->compositor->visual==tr_state->visual) ) { sx = gf_divfix(sx, tr_state->visual->compositor->scale_x); sy = gf_divfix(sy, tr_state->visual->compositor->scale_y); } rc.x = - rc.width/2; rc.y = rc.height/2; tx = ty = 0; if (vp->fit && vp->alignment.count) { /*left alignment*/ if (vp->alignment.vals[0] == -1) tx = rc.width/2 - w/2; else if (vp->alignment.vals[0] == 1) tx = w/2 - rc.width/2; if (vp->alignment.count>1) { /*top-alignment*/ if (vp->alignment.vals[1]==-1) ty = rc.height/2 - h/2; else if (vp->alignment.vals[1]==1) ty = h/2 - rc.height/2; } } gf_mx2d_init(mat); if (tr_state->pixel_metrics) { gf_mx2d_add_scale(&mat, sx, sy); } else { /*if we are not in pixelMetrics, undo the meterMetrics->pixelMetrics transformation*/ gf_mx2d_add_scale(&mat, gf_divfix(sx, tr_state->min_hsize), gf_divfix(sy, tr_state->min_hsize) ); } gf_mx2d_add_translation(&mat, tx, ty); gf_mx2d_add_translation(&mat, -gf_mulfix(vp->position.x,sx), -gf_mulfix(vp->position.y,sy) ); gf_mx2d_add_rotation(&mat, 0, 0, vp->orientation); tr_state->bounds = rc; tr_state->bounds.x += tx; tr_state->bounds.y += ty; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { /*in layers directly modify the model matrix*/ if (tr_state->is_layer) { gf_mx_from_mx2d(&mx, &mat); gf_mx_add_matrix(&tr_state->model_matrix, &mx); } /*otherwise add to camera viewport matrix*/ else { gf_mx_from_mx2d(&tr_state->camera->viewport, &mat); tr_state->camera->flags = (CAM_HAS_VIEWPORT | CAM_IS_DIRTY); } } else #endif gf_mx2d_pre_multiply(&tr_state->transform, &mat); }
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); }
void gf_sc_get_nodes_bounds(GF_Node *self, GF_ChildNodeItem *children, GF_TraverseState *tr_state, s32 *child_idx) { u32 i; SFVec2f size; GF_Rect rc; GF_Matrix2D cur_mx; if (tr_state->abort_bounds_traverse) { if (self == tr_state->for_node) { gf_mx2d_pre_multiply(&tr_state->mx_at_node, &tr_state->transform); } tr_state->abort_bounds_traverse=0; gf_sc_get_nodes_bounds(self, children, tr_state, child_idx); tr_state->abort_bounds_traverse=1; return; } if (!children) return; size.x = size.y = -FIX_ONE; #ifndef GPAC_DISABLE_VRML switch (gf_node_get_tag(self)) { case TAG_MPEG4_Layer2D: size = ((M_Layer2D *)self)->size; break; case TAG_MPEG4_Layer3D: size = ((M_Layer3D *)self)->size; break; case TAG_MPEG4_Form: size = ((M_Form *)self)->size; break; } #endif if ((size.x>=0) && (size.y>=0)) { tr_state->bounds = gf_rect_center(size.x, size.y); return; } gf_mx2d_copy(cur_mx, tr_state->transform); rc = gf_rect_center(0,0); i = 0; while (children) { if (child_idx && (i != (u32) *child_idx)) { children = children->next; continue; } gf_mx2d_init(tr_state->transform); tr_state->bounds = gf_rect_center(0,0); /*we hit the target node*/ if (children->node == tr_state->for_node) tr_state->abort_bounds_traverse = 1; gf_node_traverse(children->node, tr_state); if (tr_state->abort_bounds_traverse) { gf_mx2d_add_matrix(&tr_state->mx_at_node, &cur_mx); return; } gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); gf_rect_union(&rc, &tr_state->bounds); children = children->next; if (child_idx) break; } #ifndef GPAC_DISABLE_SVG if (gf_node_get_tag(self)==TAG_SVG_use) { GF_FieldInfo info; if (gf_node_get_attribute_by_tag(self, TAG_XLINK_ATT_href, 0, 0, &info)==GF_OK) { GF_Node *iri = ((XMLRI*)info.far_ptr)->target; if (iri) { gf_mx2d_init(tr_state->transform); tr_state->bounds = gf_rect_center(0,0); /*we hit the target node*/ if (iri == tr_state->for_node) tr_state->abort_bounds_traverse = 1; gf_node_traverse(iri, tr_state); if (tr_state->abort_bounds_traverse) { gf_mx2d_pre_multiply(&tr_state->mx_at_node, &cur_mx); return; } gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); gf_rect_union(&rc, &tr_state->bounds); } } } #endif gf_mx2d_copy(tr_state->transform, cur_mx); if (self != tr_state->for_node) { gf_mx2d_apply_rect(&tr_state->transform, &rc); } tr_state->bounds = rc; }
/* Set the viewport of the renderer based on the element that contains a viewport TODO: change the SVG_SANI_svgElement into an element that has a viewport (more generic) */ static void gf_svg_sani_set_viewport_transformation(RenderEffect2D *eff, SVG_SANI_svgElement *svg, Bool is_root) { GF_Matrix2D mat; Fixed real_width, real_height; gf_mx2d_init(mat); if (is_root) { u32 scene_width = eff->surface->render->compositor->scene_width; u32 scene_height = eff->surface->render->compositor->scene_height; u32 dpi = 90; /* Should retrieve the dpi from the system */ switch (svg->width.type) { case SVG_NUMBER_VALUE: case SVG_NUMBER_PX: real_width = INT2FIX(scene_width); break; case SVG_NUMBER_PERCENTAGE: /*u32 * fixed / u32*/ real_width = scene_width*svg->width.value/100; break; case SVG_NUMBER_IN: real_width = dpi * svg->width.value; break; case SVG_NUMBER_CM: real_width = gf_mulfix(dpi*FLT2FIX(0.39), svg->width.value); break; case SVG_NUMBER_MM: real_width = gf_mulfix(dpi*FLT2FIX(0.039), svg->width.value); break; case SVG_NUMBER_PT: real_width = dpi/12 * svg->width.value; break; case SVG_NUMBER_PC: real_width = dpi/6 * svg->width.value; break; default: real_width = INT2FIX(scene_width); break; } switch (svg->height.type) { case SVG_NUMBER_VALUE: case SVG_NUMBER_PX: real_height = INT2FIX(scene_height); break; case SVG_NUMBER_PERCENTAGE: real_height = scene_height*svg->height.value/100; break; case SVG_NUMBER_IN: real_height = dpi * svg->height.value; break; case SVG_NUMBER_CM: real_height = gf_mulfix(dpi*FLT2FIX(0.39), svg->height.value); break; case SVG_NUMBER_MM: real_height = gf_mulfix(dpi*FLT2FIX(0.039), svg->height.value); break; case SVG_NUMBER_PT: real_height = dpi/12 * svg->height.value; break; case SVG_NUMBER_PC: real_height = dpi/6 * svg->height.value; break; default: real_height = INT2FIX(scene_height); break; } } else { real_width = real_height = 0; switch (svg->width.type) { case SVG_NUMBER_VALUE: case SVG_NUMBER_PX: real_width = svg->width.value; break; default: break; } switch (svg->height.type) { case SVG_NUMBER_VALUE: case SVG_NUMBER_PX: real_height = svg->height.value; break; default: break; } } if (!real_width || !real_height) return; if (svg->viewBox.is_set && svg->viewBox.width != 0 && svg->viewBox.height != 0) { Fixed scale, vp_w, vp_h; if (svg->preserveAspectRatio.meetOrSlice==SVG_MEETORSLICE_MEET) { if (gf_divfix(real_width, svg->viewBox.width) > gf_divfix(real_height, svg->viewBox.height)) { scale = gf_divfix(real_height, svg->viewBox.height); vp_w = gf_mulfix(svg->viewBox.width, scale); vp_h = real_height; } else { scale = gf_divfix(real_width, svg->viewBox.width); vp_w = real_width; vp_h = gf_mulfix(svg->viewBox.height, scale); } } else { if (gf_divfix(real_width, svg->viewBox.width) < gf_divfix(real_height, svg->viewBox.height)) { scale = gf_divfix(real_height, svg->viewBox.height); vp_w = gf_mulfix(svg->viewBox.width, scale); vp_h = real_height; } else { scale = gf_divfix(real_width, svg->viewBox.width); vp_w = real_width; vp_h = gf_mulfix(svg->viewBox.height, scale); } } if (svg->preserveAspectRatio.align==SVG_PRESERVEASPECTRATIO_NONE) { mat.m[0] = gf_divfix(real_width, svg->viewBox.width); mat.m[4] = gf_divfix(real_height, svg->viewBox.height); mat.m[2] = - gf_muldiv(svg->viewBox.x, real_width, svg->viewBox.width); mat.m[5] = - gf_muldiv(svg->viewBox.y, real_height, svg->viewBox.height); } else { Fixed dx, dy; mat.m[0] = mat.m[4] = scale; mat.m[2] = - gf_mulfix(svg->viewBox.x, scale); mat.m[5] = - gf_mulfix(svg->viewBox.y, scale); dx = dy = 0; switch (svg->preserveAspectRatio.align) { case SVG_PRESERVEASPECTRATIO_XMINYMIN: break; case SVG_PRESERVEASPECTRATIO_XMIDYMIN: dx = ( real_width - vp_w) / 2; break; case SVG_PRESERVEASPECTRATIO_XMAXYMIN: dx = real_width - vp_w; break; case SVG_PRESERVEASPECTRATIO_XMINYMID: dy = ( real_height - vp_h) / 2; break; case SVG_PRESERVEASPECTRATIO_XMIDYMID: dx = ( real_width - vp_w) / 2; dy = ( real_height - vp_h) / 2; break; case SVG_PRESERVEASPECTRATIO_XMAXYMID: dx = real_width - vp_w; dy = ( real_height - vp_h) / 2; break; case SVG_PRESERVEASPECTRATIO_XMINYMAX: dy = real_height - vp_h; break; case SVG_PRESERVEASPECTRATIO_XMIDYMAX: dx = (real_width - vp_w) / 2; dy = real_height - vp_h; break; case SVG_PRESERVEASPECTRATIO_XMAXYMAX: dx = real_width - vp_w; dy = real_height - vp_h; break; } mat.m[2] += dx; mat.m[5] += dy; /*we need a clipper*/ if (svg->preserveAspectRatio.meetOrSlice==SVG_MEETORSLICE_SLICE) { GF_Rect rc; rc.width = real_width; rc.height = real_height; if (!is_root) { rc.x = 0; rc.y = real_height; gf_mx2d_apply_rect(&eff->vb_transform, &rc); } else { rc.x = dx; rc.y = dy + real_height; } eff->surface->top_clipper = gf_rect_pixelize(&rc); } } } gf_mx2d_pre_multiply(&eff->vb_transform, &mat); }
static void svg_traverse_resource(GF_Node *node, void *rs, Bool is_destroy, Bool is_foreign_object) { GF_Matrix2D backup_matrix; GF_Matrix mx_3d; GF_Matrix2D translate; SVGPropertiesPointers backup_props; u32 backup_flags, dirty; Bool is_fragment; GF_Node *used_node; GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVGAllAttributes all_atts; SVGlinkStack *stack = gf_node_get_private(node); SFVec2f prev_vp; SVG_Number *prev_opacity; if (is_destroy) { if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); gf_free(stack); return; } gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!all_atts.xlink_href) return; if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) return; dirty = gf_node_dirty_get(node); if (dirty & GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node); if (dirty & GF_SG_SVG_XLINK_HREF_DIRTY) { stack->fragment_id = NULL; stack->inline_sg = NULL; if (all_atts.xlink_href->string && (all_atts.xlink_href->string[0]=='#')) { stack->fragment_id = all_atts.xlink_href->string; stack->inline_sg = gf_node_get_graph(node); } else { GF_MediaObject *new_res = gf_mo_load_xlink_resource(node, is_foreign_object, 0, -1); if (new_res != stack->resource) { if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); stack->resource = new_res; } } } gf_node_dirty_clear(node, 0); /*locate the used node - this is done at each step to handle progressive loading*/ is_fragment = 0; used_node = NULL; if (!stack->inline_sg && !stack->fragment_id && all_atts.xlink_href) { if (all_atts.xlink_href->type == XMLRI_ELEMENTID) { used_node = all_atts.xlink_href->target; is_fragment = 1; } else if (stack->resource) { stack->inline_sg = gf_mo_get_scenegraph(stack->resource); if (!is_foreign_object) { stack->fragment_id = strchr(all_atts.xlink_href->string, '#'); } } } if (!used_node && stack->inline_sg) { if (stack->fragment_id) { used_node = gf_sg_find_node_by_name(stack->inline_sg, (char *) stack->fragment_id+1); is_fragment = 1; } else if (is_foreign_object) { used_node = gf_sg_get_root_node(stack->inline_sg); } } if (!used_node) goto end; /*stack use nodes for picking*/ gf_list_add(tr_state->use_stack, used_node); gf_list_add(tr_state->use_stack, node); gf_mx2d_init(translate); translate.m[2] = (all_atts.x ? all_atts.x->value : 0); translate.m[5] = (all_atts.y ? all_atts.y->value : 0); /*update VP size (SVG 1.1)*/ prev_vp = tr_state->vp_size; if (all_atts.width && all_atts.height) { tr_state->vp_size.x = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.width); tr_state->vp_size.y = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.height); } prev_opacity = tr_state->parent_use_opacity; tr_state->parent_use_opacity = all_atts.opacity; if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); if (!compositor_svg_is_display_off(tr_state->svg_props)) { gf_node_traverse(used_node, tr_state); gf_mx2d_apply_rect(&translate, &tr_state->bounds); } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); } /*SORT mode and visible, traverse*/ else if (!compositor_svg_is_display_off(tr_state->svg_props) && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN)) { compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { gf_mx_add_matrix_2d(&tr_state->model_matrix, &translate); if (tr_state->traversing_mode==TRAVERSE_SORT) { GF_Matrix tmp; gf_mx_from_mx2d(&tmp, &translate); visual_3d_matrix_add(tr_state->visual, tmp.m); } } else #endif gf_mx2d_pre_multiply(&tr_state->transform, &translate); drawable_check_focus_highlight(node, tr_state, NULL); if (is_fragment) { gf_node_traverse(used_node, tr_state); } else { gf_sc_traverse_subscene(tr_state->visual->compositor, node, stack->inline_sg, tr_state); } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); } gf_list_rem_last(tr_state->use_stack); gf_list_rem_last(tr_state->use_stack); tr_state->vp_size = prev_vp; tr_state->parent_use_opacity = prev_opacity; end: memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
static void svg_traverse_svg(GF_Node *node, void *rs, Bool is_destroy) { Bool rootmost_svg, send_resize; u32 viewport_color; SVGsvgStack *stack; GF_Matrix2D backup_matrix, vb_bck; #ifndef GPAC_DISABLE_3D GF_Matrix bck_mx; #endif Bool is_dirty; GF_IRect top_clip; SFVec2f prev_vp; SVGPropertiesPointers backup_props, *prev_props; u32 backup_flags; Bool invalidate_flag; u32 styling_size = sizeof(SVGPropertiesPointers); GF_TraverseState *tr_state = (GF_TraverseState *) rs; SVGAllAttributes all_atts; stack = gf_node_get_private(node); if (is_destroy) { if (stack->svg_props) { gf_svg_properties_reset_pointers(stack->svg_props); gf_free(stack->svg_props); } gf_sc_check_focus_upon_destroy(node); if (stack->vp_fill) drawable_del(stack->vp_fill); gf_free(stack); return; } prev_props = tr_state->svg_props; /*SVG props not set: we are either the root-most <svg> of the compositor or an <svg> inside an <animation>*/ if (!tr_state->svg_props) { tr_state->svg_props = stack->svg_props; if (!tr_state->svg_props) return; } gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) { tr_state->svg_props = prev_props; return; } /*enable or disable navigation*/ tr_state->visual->compositor->navigation_disabled = (all_atts.zoomAndPan && *all_atts.zoomAndPan == SVG_ZOOMANDPAN_DISABLE) ? 1 : 0; if (compositor_svg_is_display_off(tr_state->svg_props)) { memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; return; } top_clip = tr_state->visual->top_clipper; gf_mx2d_copy(backup_matrix, tr_state->transform); gf_mx2d_copy(vb_bck, tr_state->vb_transform); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) gf_mx_copy(bck_mx, tr_state->model_matrix); #endif invalidate_flag = tr_state->invalidate_all; is_dirty = gf_node_dirty_get(node); if (is_dirty & GF_SG_CHILD_DIRTY) drawable_reset_group_highlight(tr_state, node); gf_node_dirty_clear(node, 0); send_resize = 0; if ((stack->parent_vp.x != tr_state->vp_size.x) || (stack->parent_vp.y != tr_state->vp_size.y)) { is_dirty = 1; send_resize = 1; } if (is_dirty || tr_state->visual->compositor->recompute_ar) { svg_recompute_viewport_transformation(node, stack, tr_state, &all_atts); } gf_mx2d_copy(tr_state->vb_transform, stack->viewbox_mx); rootmost_svg = (stack->root_svg && !tr_state->parent_anim_atts) ? 1 : 0; if (tr_state->traversing_mode == TRAVERSE_SORT) { SVG_Paint *vp_fill = NULL; Fixed vp_opacity; if (tr_state->parent_anim_atts) { vp_fill = tr_state->parent_anim_atts->viewport_fill; vp_opacity = tr_state->parent_anim_atts->viewport_fill_opacity ? tr_state->parent_anim_atts->viewport_fill_opacity->value : FIX_ONE; } else { vp_fill = tr_state->svg_props->viewport_fill; vp_opacity = tr_state->svg_props->viewport_fill_opacity ? tr_state->svg_props->viewport_fill_opacity->value : FIX_ONE; } if (vp_fill && (vp_fill->type != SVG_PAINT_NONE) && vp_opacity) { Bool col_dirty = 0; viewport_color = GF_COL_ARGB_FIXED(vp_opacity, vp_fill->color.red, vp_fill->color.green, vp_fill->color.blue); if (stack->prev_color != viewport_color) { stack->prev_color = viewport_color; col_dirty = 1; } if (!rootmost_svg) { DrawableContext *ctx; Fixed width = tr_state->parent_anim_atts->width->value; Fixed height = tr_state->parent_anim_atts->height->value; if (!stack->vp_fill) { stack->vp_fill = drawable_new(); stack->vp_fill->node = node; } if ((width != stack->vp_fill->path->bbox.width) || (height != stack->vp_fill->path->bbox.height)) { drawable_reset_path(stack->vp_fill); gf_path_add_rect(stack->vp_fill->path, 0, 0, width, -height); } ctx = drawable_init_context_svg(stack->vp_fill, tr_state); if (ctx) { ctx->flags &= ~CTX_IS_TRANSPARENT; ctx->aspect.pen_props.width = 0; ctx->aspect.fill_color = viewport_color; ctx->aspect.fill_texture = NULL; if (col_dirty) ctx->flags |= CTX_APP_DIRTY; drawable_finalize_sort(ctx, tr_state, NULL); } } else if (col_dirty) { tr_state->visual->compositor->back_color = viewport_color; /*invalidate the entire visual*/ tr_state->invalidate_all = 1; } } } if (!stack->root_svg && (all_atts.x || all_atts.y)) gf_mx2d_add_translation(&tr_state->vb_transform, all_atts.x->value, all_atts.y->value); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (tr_state->traversing_mode==TRAVERSE_SORT) { GF_Matrix tmp; visual_3d_matrix_push(tr_state->visual); gf_mx_from_mx2d(&tmp, &tr_state->vb_transform); visual_3d_matrix_add(tr_state->visual, tmp.m); } else { gf_mx_add_matrix_2d(&tr_state->model_matrix, &tr_state->vb_transform); } } else #endif { gf_mx2d_pre_multiply(&tr_state->transform, &tr_state->vb_transform); } /*store VP and move it to current VP (eg, the one used to compute the vb_transform)*/ prev_vp = tr_state->vp_size; tr_state->vp_size = stack->vp; /*the event may trigger scripts which may delete nodes / modify the scene. We therefore send the resize event before traversing the scene*/ if (send_resize) { GF_DOM_Event evt; memset(&evt, 0, sizeof(GF_DOM_Event)); evt.bubbles = 1; evt.type = GF_EVENT_RESIZE; gf_dom_event_fire(node, &evt); } if ((stack->vp.x != prev_vp.x) || (stack->vp.y != prev_vp.y)) { GF_Scene *scene = node->sgprivate->scenegraph->userpriv; if (scene) { GF_DOM_Event evt; memset(&evt, 0, sizeof(GF_DOM_Event)); evt.bubbles = 0; evt.screen_rect.width = stack->vpw; evt.screen_rect.height = stack->vph; evt.screen_rect.x = stack->dx; evt.screen_rect.y = stack->dy; evt.prev_translate.x = stack->vp.x; evt.prev_translate.y = stack->vp.y; evt.type = GF_EVENT_VP_RESIZE; gf_scene_notify_event(scene, 0, NULL, &evt, GF_OK); } } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { gf_sc_get_nodes_bounds(node, ((SVG_Element *)node)->children, tr_state, NULL); } else { compositor_svg_traverse_children(((SVG_Element *)node)->children, tr_state); } tr_state->vp_size = prev_vp; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (tr_state->traversing_mode==TRAVERSE_SORT) visual_3d_matrix_pop(tr_state->visual); gf_mx_copy(tr_state->model_matrix, bck_mx); } #endif gf_mx2d_copy(tr_state->transform, backup_matrix); gf_mx2d_copy(tr_state->vb_transform, vb_bck); memcpy(tr_state->svg_props, &backup_props, styling_size); tr_state->svg_flags = backup_flags; tr_state->visual->top_clipper = top_clip; if (!stack->root_svg) { tr_state->invalidate_all = invalidate_flag; } tr_state->svg_props = prev_props; }
static void svg_traverse_animation(GF_Node *node, void *rs, Bool is_destroy) { SVGAllAttributes all_atts; GF_Matrix2D backup_matrix; GF_Matrix backup_matrix3d; SVGPropertiesPointers backup_props; u32 backup_flags; SFVec2f prev_vp; GF_Rect rc; GF_IRect clip, prev_clip; SVGAllAttributes *prev_vp_atts; GF_TraverseState *tr_state = (GF_TraverseState*)rs; GF_Matrix2D translate; SVGPropertiesPointers *old_props; SVGlinkStack *stack = gf_node_get_private(node); if (is_destroy) { if (stack->resource) gf_mo_unload_xlink_resource(node, stack->resource); gf_free(stack); return; } gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!stack->inline_sg && !stack->resource) { if (!stack->init_vis_state) { if (all_atts.initialVisibility && (*all_atts.initialVisibility==SVG_INITIALVISIBILTY_ALWAYS)) { stack->init_vis_state = 2; svg_animation_smil_update(node, stack, 0); } else { stack->init_vis_state = 1; } } if (!stack->inline_sg && !stack->resource) return; } if (!all_atts.width || !all_atts.height) return; if (!all_atts.width->value || !all_atts.height->value) return; if (!compositor_svg_traverse_base(node, &all_atts, tr_state, &backup_props, &backup_flags)) return; if (compositor_svg_is_display_off(tr_state->svg_props) || *(tr_state->svg_props->visibility) == SVG_VISIBILITY_HIDDEN) { goto end; } compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &backup_matrix3d); /*add x/y translation*/ gf_mx2d_init(translate); translate.m[2] = (all_atts.x ? all_atts.x->value : 0); translate.m[5] = (all_atts.y ? all_atts.y->value : 0); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { gf_mx_add_matrix_2d(&tr_state->model_matrix, &translate); if (tr_state->traversing_mode==TRAVERSE_SORT) { GF_Matrix tmp; gf_mx_from_mx2d(&tmp, &translate); visual_3d_matrix_add(tr_state->visual, tmp.m); } } else #endif gf_mx2d_pre_multiply(&tr_state->transform, &translate); /*reset SVG props to reload a new inheritance context*/ old_props = tr_state->svg_props; tr_state->svg_props = NULL; /*store this node's attribute to compute PAR/ViewBox of the child <svg>*/ prev_vp_atts = tr_state->parent_anim_atts; tr_state->parent_anim_atts = &all_atts; /*update VP size*/ prev_vp = tr_state->vp_size; tr_state->vp_size.x = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.width); tr_state->vp_size.y = gf_sc_svg_convert_length_to_display(tr_state->visual->compositor, all_atts.height); /*setup new clipper*/ rc.width = tr_state->vp_size.x; rc.height = tr_state->vp_size.y; rc.x = 0; rc.y = tr_state->vp_size.y; gf_mx2d_apply_rect(&tr_state->transform, &rc); prev_clip = tr_state->visual->top_clipper; clip = gf_rect_pixelize(&rc); // gf_irect_intersect(&tr_state->visual->top_clipper, &clip); if (!stack->inline_sg && stack->resource) { stack->inline_sg = gf_mo_get_scenegraph(stack->resource); } if (stack->inline_sg) { gf_sc_traverse_subscene(tr_state->visual->compositor, node, stack->inline_sg, tr_state); } if (stack->init_vis_state == 2) { stack->init_vis_state = 3; gf_mo_pause(stack->resource); } tr_state->svg_props = old_props; tr_state->visual->top_clipper = prev_clip; tr_state->parent_anim_atts = prev_vp_atts; tr_state->vp_size = prev_vp; compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &backup_matrix3d); end: memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }