void get_gf_sr_texture_transform(GF_Node *__appear, GF_TextureHandler *txh, GF_Matrix2D *mat, Bool line_texture, Fixed final_width, Fixed final_height) { u32 node_tag; M_Appearance *appear; GF_Node *txtrans = NULL; gf_mx2d_init(*mat); if (!__appear || !txh) return; appear = (M_Appearance *)__appear; if (!line_texture) { if (!appear->textureTransform) return; txtrans = appear->textureTransform; } else { if (gf_node_get_tag(appear->material) != TAG_MPEG4_Material2D) return; if (gf_node_get_tag(((M_Material2D *)appear->material)->lineProps) != TAG_MPEG4_XLineProperties) return; txtrans = ((M_XLineProperties *) ((M_Material2D *)appear->material)->lineProps)->textureTransform; } if (!txtrans) return; /*gradient doesn't need bounds info in texture transform*/ if (txh->compute_gradient_matrix) { final_width = final_height = FIX_ONE; } node_tag = gf_node_get_tag(txtrans); if (node_tag==TAG_MPEG4_TextureTransform) { /*VRML: Tc' = -C × S × R × C × T × Tc*/ M_TextureTransform *txt = (M_TextureTransform *) txtrans; SFVec2f scale = txt->scale; if (!scale.x) scale.x = FIX_ONE/100; if (!scale.y) scale.y = FIX_ONE/100; gf_mx2d_add_translation(mat, -txt->center.x * final_width, -txt->center.y * final_height); gf_mx2d_add_scale(mat, scale.x, scale.y); gf_mx2d_add_rotation(mat, 0, 0, txt->rotation); gf_mx2d_add_translation(mat, txt->center.x * final_width, txt->center.y * final_height); gf_mx2d_add_translation(mat, txt->translation.x * final_width, txt->translation.y * final_height); /*and inverse the matrix (this is texture transform, cf VRML)*/ gf_mx2d_inverse(mat); return; } if (node_tag==TAG_MPEG4_TransformMatrix2D) { TM2D_GetMatrix((GF_Node *) txtrans, mat); mat->m[2] *= final_width; mat->m[5] *= final_height; gf_mx2d_inverse(mat); return; } }
static void svg_sani_LG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat) { SFVec2f start, end; SVG_SANI_linearGradientElement *lg = (SVG_SANI_linearGradientElement *) txh->owner; /*create gradient brush if needed*/ if (!txh->hwtx) return; start.x = lg->x1.value; if (lg->x1.type==SVG_NUMBER_PERCENTAGE) start.x /= 100; start.y = lg->y1.value; if (lg->y1.type==SVG_NUMBER_PERCENTAGE) start.y /= 100; end.x = lg->x2.value; if (lg->x2.type==SVG_NUMBER_PERCENTAGE) end.x /= 100; end.y = lg->y2.value; if (lg->y2.type==SVG_NUMBER_PERCENTAGE) end.x /= 100; txh->compositor->r2d->stencil_set_gradient_mode(txh->hwtx, (GF_GradientMode) lg->spreadMethod); gf_mx2d_copy(*mat, lg->gradientTransform.mat); if (lg->gradientUnits==SVG_GRADIENTUNITS_OBJECT) { /*move to local coord system - cf SVG spec*/ gf_mx2d_add_scale(mat, bounds->width, bounds->height); gf_mx2d_add_translation(mat, bounds->x - 1, bounds->y - bounds->height - 1); } txh->compositor->r2d->stencil_set_linear_gradient(txh->hwtx, start.x, start.y, end.x, end.y); }
static void TraverseTransform2D(GF_Node *node, void *rs, Bool is_destroy) { M_Transform2D *tr = (M_Transform2D *)node; Transform2DStack *ptr = (Transform2DStack *)gf_node_get_private(node); GF_TraverseState *tr_state; if (is_destroy) { gf_sc_check_focus_upon_destroy(node); group_2d_destroy(node, (GroupingNode2D*)ptr); gf_free(ptr); return; } tr_state = (GF_TraverseState *) rs; if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { gf_mx2d_init(ptr->mat); ptr->is_identity = 1; if ((tr->scale.x != FIX_ONE) || (tr->scale.y != FIX_ONE)) { gf_mx2d_add_scale_at(&ptr->mat, tr->scale.x, tr->scale.y, 0, 0, tr->scaleOrientation); ptr->is_identity = 0; } if (tr->rotationAngle) { gf_mx2d_add_rotation(&ptr->mat, tr->center.x, tr->center.y, tr->rotationAngle); ptr->is_identity = 0; } if (tr->translation.x || tr->translation.y) { ptr->is_identity = 0; gf_mx2d_add_translation(&ptr->mat, tr->translation.x, tr->translation.y); } gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); ptr->is_null = (!tr->scale.x || !tr->scale.y) ? 1 : 0; } traverse_transform(node, ptr, tr_state); }
static void LG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat) { SFVec2f start, end; M_LinearGradient *lg = (M_LinearGradient *) txh->owner; if (lg->key.count<2) return; if (lg->key.count != lg->keyValue.count) return; start = lg->startPoint; end = lg->endPoint; /*create gradient brush if needed*/ if (!txh->hwtx) return; GradientGetMatrix((GF_Node *) lg->transform, mat); /*move line to object space*/ start.x = gf_mulfix(start.x, bounds->width); end.x = gf_mulfix(end.x, bounds->width); start.y = gf_mulfix(start.y, bounds->height); end.y = gf_mulfix(end.y, bounds->height); /*move transform to object space*/ mat->m[2] = gf_mulfix(mat->m[2], bounds->width); mat->m[5] = gf_mulfix(mat->m[5], bounds->height); mat->m[1] = gf_muldiv(mat->m[1], bounds->width, bounds->height); mat->m[3] = gf_muldiv(mat->m[3], bounds->height, bounds->width); /*translate to the center of the bounds*/ gf_mx2d_add_translation(mat, bounds->x, bounds->y - bounds->height); txh->compositor->r2d->stencil_set_linear_gradient(txh->hwtx, start.x, start.y, end.x, end.y); }
GF_Node *CT2D_PickNode(GF_TextureHandler *txh, DrawableContext *ctx, Fixed x, Fixed y) { GF_Rect orig; GF_Matrix2D mat, tx_trans; Fixed width, height; Composite2DStack *st = (Composite2DStack *) gf_node_get_private(txh->owner); assert(st->surf); orig = ctx->bi->unclip; gf_mx2d_copy(mat, ctx->transform); gf_mx2d_inverse(&mat); gf_mx2d_apply_rect(&mat, &orig); gf_mx2d_init(mat); gf_mx2d_add_scale(&mat, orig.width / st->width, orig.height / st->height); get_gf_sr_texture_transform(ctx->appear, &st->txh, &tx_trans, (ctx->h_texture==&st->txh) ? 0 : 1, INT2FIX(orig.width), INT2FIX(orig.height)); gf_mx2d_add_matrix(&mat, &tx_trans); gf_mx2d_add_translation(&mat, (orig.x), (orig.y - orig.height)); gf_mx2d_add_matrix(&mat, &ctx->transform); gf_mx2d_inverse(&mat); gf_mx2d_apply_coords(&mat, &x, &y); width = INT2FIX(st->width); height = INT2FIX(st->height); while (x>width) x -= width; while (x < 0) x += width; while (y>height) y -= height; while (y < 0) y += height; x -= width / 2; y -= height / 2; return VS2D_PickNode(st->surf, x, y); }
static void svg_sani_rG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat) { SFVec2f center, focal; Fixed radius; SVG_SANI_radialGradientElement *rg = (SVG_SANI_radialGradientElement *) txh->owner; /*create gradient brush if needed*/ if (!txh->hwtx) return; gf_mx2d_copy(*mat, rg->gradientTransform.mat); radius = rg->r.value; if (rg->r.type==SVG_NUMBER_PERCENTAGE) radius /= 100; center.x = rg->cx.value; if (rg->cx.type==SVG_NUMBER_PERCENTAGE) center.x /= 100; center.y = rg->cy.value; if (rg->cy.type==SVG_NUMBER_PERCENTAGE) center.y /= 100; txh->compositor->r2d->stencil_set_gradient_mode(txh->hwtx, (GF_GradientMode) rg->spreadMethod); focal.x = rg->fx.value; if (rg->fx.type==SVG_NUMBER_PERCENTAGE) focal.x /= 100; focal.y = rg->fy.value; if (rg->fy.type==SVG_NUMBER_PERCENTAGE) focal.y /= 100; if (rg->gradientUnits==SVG_GRADIENTUNITS_OBJECT) { /*move to local coord system - cf SVG spec*/ gf_mx2d_add_scale(mat, bounds->width, bounds->height); gf_mx2d_add_translation(mat, bounds->x, bounds->y - bounds->height); } else if ((rg->fx.value==rg->fy.value) && (rg->fx.value==FIX_ONE/2)) { focal.x = center.x; focal.y = center.y; } txh->compositor->r2d->stencil_set_radial_gradient(txh->hwtx, center.x, center.y, focal.x, focal.y, radius, radius); }
void VS2D_TexturePathText(VisualSurface2D *surf, DrawableContext *txt_ctx, GF_Path *path, GF_Rect *object_bounds, GF_HWTEXTURE hwtx, GF_Rect *gf_sr_texture_bounds) { Fixed sS, sT; GF_Matrix2D gf_mx2d_txt; GF_Rect rc, orig_rc; u8 alpha, r, g, b; GF_ColorMatrix cmat; GF_Raster2D *r2d = surf->render->compositor->r2d; VS2D_SetOptions(surf->render, surf->the_surface, 0, 1); /*get original bounds*/ orig_rc = *object_bounds; rc = *gf_sr_texture_bounds; /*get scaling ratio so that active texture view is stretched to original bounds (std 2D shape texture mapping in MPEG4)*/ sS = gf_divfix(orig_rc.width, rc.width); sT = gf_divfix(orig_rc.height, rc.height); gf_mx2d_init(gf_mx2d_txt); gf_mx2d_add_scale(&gf_mx2d_txt, sS, sT); /*move to bottom-left corner of bounds */ gf_mx2d_add_translation(&gf_mx2d_txt, (orig_rc.x), (orig_rc.y - orig_rc.height)); /*move to final coordinate system*/ gf_mx2d_add_matrix(&gf_mx2d_txt, &txt_ctx->transform); /*set path transform, except for background2D node which is directly build in the final coord system*/ r2d->stencil_set_matrix(hwtx, &gf_mx2d_txt); alpha = GF_COL_A(txt_ctx->aspect.fill_color); r = GF_COL_R(txt_ctx->aspect.fill_color); g = GF_COL_G(txt_ctx->aspect.fill_color); b = GF_COL_B(txt_ctx->aspect.fill_color); /*if col do a cxmatrix*/ if (!r && !g && !b) { r2d->stencil_set_texture_alpha(hwtx, alpha); } else { r2d->stencil_set_texture_alpha(hwtx, 0xFF); memset(cmat.m, 0, sizeof(Fixed) * 20); cmat.m[4] = INT2FIX(r)/255; cmat.m[9] = INT2FIX(g)/255; cmat.m[14] = INT2FIX(b)/255; cmat.m[18] = INT2FIX(alpha)/255; cmat.identity = 0; r2d->stencil_set_color_matrix(hwtx, &cmat); } r2d->surface_set_matrix(surf->the_surface, &txt_ctx->transform); /*push path*/ r2d->surface_set_path(surf->the_surface, path); VS2D_DoFill(surf, txt_ctx, hwtx); r2d->surface_set_path(surf->the_surface, NULL); txt_ctx->flags |= CTX_PATH_FILLED; }
static void get_surface_world_matrix(EVGSurface *_this, GF_Matrix2D *mat) { gf_mx2d_init(*mat); if (_this->center_coords) { gf_mx2d_add_scale(mat, FIX_ONE, -FIX_ONE); gf_mx2d_add_translation(mat, INT2FIX(_this->width / 2), INT2FIX(_this->height / 2)); } }
GF_EXPORT GF_Err gf_path_add_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed fa_x, Fixed fa_y, Fixed fb_x, Fixed fb_y, Bool cw) { GF_Matrix2D mat, inv; Fixed angle, start_angle, end_angle, sweep, axis_w, axis_h, tmp, cx, cy, _vx, _vy, start_x, start_y; s32 i, num_steps; if (!gp->n_points) return GF_BAD_PARAM; start_x = gp->points[gp->n_points-1].x; start_y = gp->points[gp->n_points-1].y; cx = (fb_x + fa_x)/2; cy = (fb_y + fa_y)/2; angle = gf_atan2(fb_y-fa_y, fb_x-fa_x); gf_mx2d_init(mat); gf_mx2d_add_rotation(&mat, 0, 0, angle); gf_mx2d_add_translation(&mat, cx, cy); gf_mx2d_copy(inv, mat); gf_mx2d_inverse(&inv); gf_mx2d_apply_coords(&inv, &start_x, &start_y); gf_mx2d_apply_coords(&inv, &end_x, &end_y); gf_mx2d_apply_coords(&inv, &fa_x, &fa_y); gf_mx2d_apply_coords(&inv, &fb_x, &fb_y); //start angle and end angle start_angle = gf_atan2(start_y, start_x); end_angle = gf_atan2(end_y, end_x); tmp = gf_mulfix((start_x - fa_x), (start_x - fa_x)) + gf_mulfix((start_y - fa_y), (start_y - fa_y)); axis_w = gf_sqrt(tmp); tmp = gf_mulfix((start_x - fb_x) , (start_x - fb_x)) + gf_mulfix((start_y - fb_y), (start_y - fb_y)); axis_w += gf_sqrt(tmp); axis_w /= 2; axis_h = gf_sqrt(gf_mulfix(axis_w, axis_w) - gf_mulfix(fa_x,fa_x)); sweep = end_angle - start_angle; if (cw) { if (sweep>0) sweep -= 2*GF_PI; } else { if (sweep<0) sweep += 2*GF_PI; } num_steps = GF_2D_DEFAULT_RES/2; for (i=1; i<=num_steps; i++) { angle = start_angle + sweep*i/num_steps; _vx = gf_mulfix(axis_w, gf_cos(angle)); _vy = gf_mulfix(axis_h, gf_sin(angle)); /*re-invert*/ gf_mx2d_apply_coords(&mat, &_vx, &_vy); gf_path_add_line_to(gp, _vx, _vy); } return GF_OK; }
static void visual_2d_draw_gradient(GF_VisualManager *visual, GF_Path *path, GF_TextureHandler *txh, struct _drawable_context *ctx, GF_TraverseState *tr_state, GF_Matrix2D *ext_mx, GF_Rect *orig_bounds) { GF_Rect rc; GF_STENCIL stencil; GF_Matrix2D g_mat; GF_Raster2D *raster = visual->compositor->rasterizer; if (!txh) txh = ctx->aspect.fill_texture; gf_path_get_bounds(path, &rc); if (!rc.width || !rc.height || !txh->tx_io) return; if (orig_bounds) { txh->compute_gradient_matrix(txh, orig_bounds, &g_mat, 0); } else { txh->compute_gradient_matrix(txh, &rc, &g_mat, 0); } stencil = gf_sc_texture_get_stencil(txh); if (!stencil) return; #ifndef GPAC_DISABLE_VRML if (ctx->flags & CTX_HAS_APPEARANCE) { GF_Matrix2D txt_mat; visual_2d_get_texture_transform(ctx->appear, txh, &txt_mat, (txh == ctx->aspect.fill_texture) ? 0 : 1, INT2FIX(txh->width), INT2FIX(txh->height)); gf_mx2d_add_matrix(&g_mat, &txt_mat); } #endif /*move to bottom-left corner of bounds */ if (ext_mx) gf_mx2d_add_matrix(&g_mat, ext_mx); if (orig_bounds) gf_mx2d_add_translation(&g_mat, (orig_bounds->x), (orig_bounds->y - orig_bounds->height)); gf_mx2d_add_matrix(&g_mat, &ctx->transform); raster->stencil_set_matrix(stencil, &g_mat); raster->stencil_set_color_matrix(stencil, ctx->col_mat); /*MPEG-4/VRML context or no fill info*/ if (ctx->flags & CTX_HAS_APPEARANCE || !ctx->aspect.fill_color) raster->stencil_set_alpha(stencil, 0xFF); else raster->stencil_set_alpha(stencil, GF_COL_A(ctx->aspect.fill_color) ); raster->surface_set_matrix(visual->raster_surface, &ctx->transform); txh->flags |= GF_SR_TEXTURE_USED; raster->surface_set_path(visual->raster_surface, path); visual_2d_fill_path(visual, ctx, stencil, tr_state, 0); raster->surface_set_path(visual->raster_surface, NULL); ctx->flags |= CTX_PATH_FILLED; }
static void RG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Matrix2D *mat) { SFVec2f center, focal; u32 i, *cols; Fixed a; Bool const_a; M_RadialGradient *rg = (M_RadialGradient *) txh->owner; if (rg->key.count<2) return; if (rg->key.count != rg->keyValue.count) return; /*create gradient brush if needed*/ if (!txh->hwtx) return; GradientGetMatrix((GF_Node *) rg->transform, mat); center = rg->center; focal = rg->focalPoint; /*move circle to object space*/ center.x = gf_mulfix(center.x, bounds->width); center.y = gf_mulfix(center.y, bounds->height); focal.x = gf_mulfix(focal.x, bounds->width); focal.y = gf_mulfix(focal.y, bounds->height); /*move transform to object space*/ mat->m[2] = gf_mulfix(mat->m[2], bounds->width); mat->m[5] = gf_mulfix(mat->m[5], bounds->height); mat->m[1] = gf_muldiv(mat->m[1], bounds->width, bounds->height); mat->m[3] = gf_muldiv(mat->m[3], bounds->height, bounds->width); txh->compositor->r2d->stencil_set_radial_gradient(txh->hwtx, center.x, center.y, focal.x, focal.y, gf_mulfix(rg->radius, bounds->width), gf_mulfix(rg->radius, bounds->height)); const_a = (rg->opacity.count == 1) ? 1 : 0; cols = (u32*)malloc(sizeof(u32) * rg->key.count); for (i=0; i<rg->key.count; i++) { a = (const_a ? rg->opacity.vals[0] : rg->opacity.vals[i]); cols[i] = GF_COL_ARGB_FIXED(a, rg->keyValue.vals[i].red, rg->keyValue.vals[i].green, rg->keyValue.vals[i].blue); } txh->compositor->r2d->stencil_set_gradient_interpolation(txh->hwtx, rg->key.vals, cols, rg->key.count); free(cols); txh->compositor->r2d->stencil_set_gradient_mode(txh->hwtx, (GF_GradientMode) rg->spreadMethod); gf_mx2d_add_translation(mat, bounds->x, bounds->y - bounds->height); }
/* 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; }
static void RenderTransform2D(GF_Node *node, void *rs, Bool is_destroy) { GF_Matrix2D bckup; M_Transform2D *tr = (M_Transform2D *)node; Transform2DStack *ptr = (Transform2DStack *)gf_node_get_private(node); RenderEffect2D *eff; if (is_destroy) { DeleteGroupingNode2D((GroupingNode2D *)ptr); free(ptr); return; } eff = (RenderEffect2D *) rs; if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { gf_mx2d_init(ptr->mat); ptr->is_identity = 1; if ((tr->scale.x != FIX_ONE) || (tr->scale.y != FIX_ONE)) { gf_mx2d_add_scale_at(&ptr->mat, tr->scale.x, tr->scale.y, 0, 0, tr->scaleOrientation); ptr->is_identity = 0; } if (tr->rotationAngle) { gf_mx2d_add_rotation(&ptr->mat, tr->center.x, tr->center.y, tr->rotationAngle); ptr->is_identity = 0; } if (tr->translation.x || tr->translation.y) { ptr->is_identity = 0; gf_mx2d_add_translation(&ptr->mat, tr->translation.x, tr->translation.y); } } /*note we don't clear dirty flag, this is done in traversing*/ if (ptr->is_identity) { group2d_traverse((GroupingNode2D *)ptr, tr->children, eff); } else { gf_mx2d_copy(bckup, eff->transform); gf_mx2d_copy(eff->transform, ptr->mat); gf_mx2d_add_matrix(&eff->transform, &bckup); group2d_traverse((GroupingNode2D *)ptr, tr->children, eff); gf_mx2d_copy(eff->transform, bckup); } }
static void GradientGetMatrix(GF_Node *transform, GF_Matrix2D *mat) { gf_mx2d_init(*mat); if (transform) { switch (gf_node_get_tag(transform) ) { case TAG_MPEG4_Transform2D: { M_Transform2D *tr = (M_Transform2D *)transform; gf_mx2d_add_scale_at(mat, 0, 0, tr->scale.x, tr->scale.y, tr->scaleOrientation); gf_mx2d_add_rotation(mat, tr->center.x, tr->center.y, tr->rotationAngle); gf_mx2d_add_translation(mat, tr->translation.x, tr->translation.y); } break; case TAG_MPEG4_TransformMatrix2D: TM2D_GetMatrix(transform, mat); break; default: break; } } }
GF_EXPORT GF_Err gf_sg_command_apply(GF_SceneGraph *graph, GF_Command *com, Double time_offset) { GF_Err e; GF_CommandField *inf; GF_FieldInfo field; GF_Node *def, *node; void *slot_ptr; if (!com || !graph) return GF_BAD_PARAM; e = GF_OK; switch (com->tag) { case GF_SG_SCENE_REPLACE: /*unregister root*/ gf_node_unregister(graph->RootNode, NULL); /*remove all protos and routes*/ while (gf_list_count(graph->routes_to_activate)) gf_list_rem(graph->routes_to_activate, 0); /*destroy all routes*/ while (gf_list_count(graph->Routes)) { GF_Route *r = (GF_Route *)gf_list_get(graph->Routes, 0); /*this will unregister the route from the graph, so don't delete the chain entry*/ gf_sg_route_del(r); } /*destroy all proto*/ while (gf_list_count(graph->protos)) { GF_Proto *p = (GF_Proto*)gf_list_get(graph->protos, 0); /*this will unregister the proto from the graph, so don't delete the chain entry*/ gf_sg_proto_del(p); } /*DO NOT TOUCH node registry*/ /*DO NOT TOUCH UNREGISTERED PROTOS*/ /*move all protos in graph*/ while (gf_list_count(com->new_proto_list)) { GF_Proto *p = (GF_Proto*)gf_list_get(com->new_proto_list, 0); gf_list_rem(com->new_proto_list, 0); gf_list_del_item(graph->unregistered_protos, p); gf_list_add(graph->protos, p); } /*assign new root (no need to register/unregister)*/ graph->RootNode = com->node; com->node = NULL; break; case GF_SG_NODE_REPLACE: if (!gf_list_count(com->command_fields)) return GF_OK; inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); e = gf_node_replace(com->node, inf->new_node, 0); if (inf->new_node) gf_node_register(inf->new_node, NULL); break; case GF_SG_MULTIPLE_REPLACE: case GF_SG_FIELD_REPLACE: { u32 j; GF_ChildNodeItem *list, *cur, *prev; j=0; while ((inf = (GF_CommandField*)gf_list_enum(com->command_fields, &j))) { e = gf_node_get_field(com->node, inf->fieldIndex, &field); if (e) return e; switch (field.fieldType) { case GF_SG_VRML_SFNODE: { node = *((GF_Node **) field.far_ptr); e = gf_node_unregister(node, com->node); *((GF_Node **) field.far_ptr) = inf->new_node; if (!e) gf_node_register(inf->new_node, com->node); break; } case GF_SG_VRML_MFNODE: gf_node_unregister_children(com->node, * ((GF_ChildNodeItem **) field.far_ptr)); * ((GF_ChildNodeItem **) field.far_ptr) = NULL; list = * ((GF_ChildNodeItem **) inf->field_ptr); prev=NULL; while (list) { cur = malloc(sizeof(GF_ChildNodeItem)); cur->next = NULL; cur->node = list->node; if (prev) { prev->next = cur; } else { * ((GF_ChildNodeItem **) field.far_ptr) = cur; } gf_node_register(list->node, com->node); prev = cur; list = list->next; } break; default: /*this is a regular field, reset it and clone - we cannot switch pointers since the original fields are NOT pointers*/ if (!gf_sg_vrml_is_sf_field(field.fieldType)) { e = gf_sg_vrml_mf_reset(field.far_ptr, field.fieldType); } if (e) return e; gf_sg_vrml_field_copy(field.far_ptr, inf->field_ptr, field.fieldType); if (field.fieldType==GF_SG_VRML_SFTIME) *(SFTime *)field.far_ptr = *(SFTime *)field.far_ptr + time_offset; break; } SG_CheckFieldChange(com->node, &field); } break; } case GF_SG_MULTIPLE_INDEXED_REPLACE: case GF_SG_INDEXED_REPLACE: { u32 sftype, i=0; while ((inf = (GF_CommandField*)gf_list_enum(com->command_fields, &i))) { e = gf_node_get_field(com->node, inf->fieldIndex, &field); if (e) return e; /*if MFNode remove the child and set new node*/ if (field.fieldType == GF_SG_VRML_MFNODE) { /*we must remove the node before in case the new node uses the same ID (not forbidden) and this command removes the last instance of the node with the same ID*/ gf_node_replace_child(com->node, (GF_ChildNodeItem**) field.far_ptr, inf->pos, inf->new_node); if (inf->new_node) gf_node_register(inf->new_node, NULL); } /*erase the field item*/ else { if ((inf->pos < 0) || ((u32) inf->pos >= ((GenMFField *) field.far_ptr)->count) ) { inf->pos = ((GenMFField *)field.far_ptr)->count - 1; /*may happen with text and default value*/ if (inf->pos < 0) { inf->pos = 0; gf_sg_vrml_mf_alloc(field.far_ptr, field.fieldType, 1); } } e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, & slot_ptr, inf->pos); if (e) return e; sftype = gf_sg_vrml_get_sf_type(field.fieldType); gf_sg_vrml_field_copy(slot_ptr, inf->field_ptr, sftype); /*note we don't add time offset, since there's no MFTime*/ } SG_CheckFieldChange(com->node, &field); } break; } case GF_SG_ROUTE_REPLACE: { GF_Route *r; char *name; r = gf_sg_route_find(graph, com->RouteID); def = gf_sg_find_node(graph, com->fromNodeID); node = gf_sg_find_node(graph, com->toNodeID); if (!node || !def) return GF_SG_UNKNOWN_NODE; name = NULL; if (r) { name = r->name; r->name = NULL; gf_sg_route_del(r); } r = gf_sg_route_new(graph, def, com->fromFieldIndex, node, com->toFieldIndex); gf_sg_route_set_id(r, com->RouteID); if (name) { gf_sg_route_set_name(r, name); free(name); } break; } case GF_SG_NODE_DELETE_EX: case GF_SG_NODE_DELETE: { if (com->node) gf_node_replace(com->node, NULL, (com->tag==GF_SG_NODE_DELETE_EX) ? 1 : 0); break; } case GF_SG_ROUTE_DELETE: { return gf_sg_route_del_by_id(graph, com->RouteID); } case GF_SG_INDEXED_DELETE: { if (!gf_list_count(com->command_fields)) return GF_OK; inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); e = gf_node_get_field(com->node, inf->fieldIndex, &field); if (e) return e; if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM; /*then we need special handling in case of a node*/ if (gf_sg_vrml_get_sf_type(field.fieldType) == GF_SG_VRML_SFNODE) { e = gf_node_replace_child(com->node, (GF_ChildNodeItem **) field.far_ptr, inf->pos, NULL); } else { if ((inf->pos < 0) || ((u32) inf->pos >= ((GenMFField *) field.far_ptr)->count) ) { inf->pos = ((GenMFField *)field.far_ptr)->count - 1; } /*this is a regular MFField, just remove the item (realloc)*/ e = gf_sg_vrml_mf_remove(field.far_ptr, field.fieldType, inf->pos); } /*deletion -> node has changed*/ if (!e) SG_CheckFieldChange(com->node, &field); break; } case GF_SG_NODE_INSERT: { if (!gf_list_count(com->command_fields)) return GF_OK; inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); e = gf_node_insert_child(com->node, inf->new_node, inf->pos); if (!e) gf_node_register(inf->new_node, com->node); if (!e) gf_node_event_out(com->node, inf->fieldIndex); if (!e) gf_node_changed(com->node, NULL); break; } case GF_SG_ROUTE_INSERT: { GF_Route *r; def = gf_sg_find_node(graph, com->fromNodeID); node = gf_sg_find_node(graph, com->toNodeID); if (!node || !def) return GF_SG_UNKNOWN_NODE; r = gf_sg_route_new(graph, def, com->fromFieldIndex, node, com->toFieldIndex); if (com->RouteID) gf_sg_route_set_id(r, com->RouteID); if (com->def_name) { gf_sg_route_set_name(r, com->def_name); free(com->def_name); com->def_name = NULL; } break; } case GF_SG_INDEXED_INSERT: { u32 sftype; if (!gf_list_count(com->command_fields)) return GF_OK; inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); e = gf_node_get_field(com->node, inf->fieldIndex, &field); if (e) return e; /*rescale the MFField and parse the SFField*/ if (field.fieldType != GF_SG_VRML_MFNODE) { if (inf->pos == -1) { e = gf_sg_vrml_mf_append(field.far_ptr, field.fieldType, & slot_ptr); } else { e = gf_sg_vrml_mf_insert(field.far_ptr, field.fieldType, & slot_ptr, inf->pos); } if (e) return e; sftype = gf_sg_vrml_get_sf_type(field.fieldType); gf_sg_vrml_field_copy(slot_ptr, inf->field_ptr, sftype); } else { if (inf->new_node) { if (inf->pos == -1) { gf_node_list_add_child( (GF_ChildNodeItem **) field.far_ptr, inf->new_node); } else { gf_node_list_insert_child((GF_ChildNodeItem **) field.far_ptr, inf->new_node, inf->pos); } gf_node_register(inf->new_node, com->node); } } if (!e) SG_CheckFieldChange(com->node, &field); break; } case GF_SG_PROTO_INSERT: /*destroy all proto*/ while (gf_list_count(com->new_proto_list)) { GF_Proto *p = (GF_Proto*)gf_list_get(com->new_proto_list, 0); gf_list_rem(com->new_proto_list, 0); gf_list_del_item(graph->unregistered_protos, p); gf_list_add(graph->protos, p); } return GF_OK; case GF_SG_PROTO_DELETE: { u32 i; for (i=0; i<com->del_proto_list_size; i++) { /*note this will check for unregistered protos, but since IDs are unique we are sure we will not destroy an unregistered one*/ GF_Proto *proto = gf_sg_find_proto(graph, com->del_proto_list[i], NULL); if (proto) gf_sg_proto_del(proto); } } return GF_OK; case GF_SG_PROTO_DELETE_ALL: /*destroy all proto*/ while (gf_list_count(graph->protos)) { GF_Proto *p = (GF_Proto*)gf_list_get(graph->protos, 0); gf_list_rem(graph->protos, 0); /*this will unregister the proto from the graph, so don't delete the chain entry*/ gf_sg_proto_del(p); } /*DO NOT TOUCH UNREGISTERED PROTOS*/ return GF_OK; /*only used by BIFS*/ case GF_SG_GLOBAL_QUANTIZER: return GF_OK; #ifndef GPAC_DISABLE_SVG /*laser commands*/ case GF_SG_LSR_NEW_SCENE: /*DO NOT TOUCH node registry*/ /*assign new root (no need to register/unregister)*/ graph->RootNode = com->node; com->node = NULL; break; case GF_SG_LSR_DELETE: if (!com->node) return GF_NON_COMPLIANT_BITSTREAM; if (!gf_list_count(com->command_fields)) { gf_node_replace(com->node, NULL, 0); gf_node_deactivate(com->node); return GF_OK; } inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); node = gf_node_list_get_child(((SVG_Element *)com->node)->children, inf->pos); if (node) { e = gf_node_replace_child(com->node, &((SVG_Element *)com->node)->children, inf->pos, NULL); gf_node_deactivate(node); } break; case GF_SG_LSR_INSERT: inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); if (!com->node || !inf) return GF_NON_COMPLIANT_BITSTREAM; if (inf->new_node) { if (inf->pos<0) gf_node_list_add_child(& ((SVG_Element *)com->node)->children, inf->new_node); else gf_node_list_insert_child(& ((SVG_Element *)com->node)->children, inf->new_node, inf->pos); gf_node_register(inf->new_node, com->node); gf_node_activate(inf->new_node); gf_node_changed(com->node, NULL); } else { /*NOT SUPPORTED*/ GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[LASeR] VALUE INSERTION NOT SUPPORTED\n")); } break; case GF_SG_LSR_ADD: case GF_SG_LSR_REPLACE: inf = (GF_CommandField*)gf_list_get(com->command_fields, 0); if (!com->node || !inf) return GF_NON_COMPLIANT_BITSTREAM; if (inf->new_node) { if (inf->pos<0) { /*if fieldIndex (eg attributeName) is set, this is children replacement*/ if (inf->fieldIndex>0) { gf_node_unregister_children_deactivate(com->node, ((SVG_Element *)com->node)->children); ((SVG_Element *)com->node)->children = NULL; gf_node_list_add_child(& ((SVG_Element *)com->node)->children, inf->new_node); gf_node_register(inf->new_node, com->node); gf_node_activate(inf->new_node); } else { e = gf_node_replace(com->node, inf->new_node, 0); gf_node_activate(inf->new_node); } } else { node = gf_node_list_get_child( ((SVG_Element *)com->node)->children, inf->pos); gf_node_replace_child(com->node, & ((SVG_Element *)com->node)->children, inf->pos, inf->new_node); gf_node_register(inf->new_node, com->node); if (node) gf_node_deactivate(node); gf_node_activate(inf->new_node); } /*signal node modif*/ gf_node_changed(com->node, NULL); return e; } else if (inf->node_list) { GF_ChildNodeItem *child, *cur, *prev; gf_node_unregister_children_deactivate(com->node, ((SVG_Element *)com->node)->children); ((SVG_Element *)com->node)->children = NULL; prev = NULL; child = inf->node_list; while (child) { cur = (GF_ChildNodeItem*)malloc(sizeof(GF_ChildNodeItem)); cur->next = NULL; cur->node = child->node; gf_node_register(child->node, com->node); gf_node_activate(child->node); if (prev) prev->next = cur; else ((SVG_Element *)com->node)->children = cur; prev = cur; child = child->next; } /*signal node modif*/ gf_node_changed(com->node, NULL); return GF_OK; } /*attribute modif*/ else if (inf->field_ptr) { GF_FieldInfo a, b; if (inf->fieldIndex==(u32) -2) { GF_Point2D scale, translate; Fixed rotate; GF_Matrix2D *dest; gf_node_get_field_by_name(com->node, "transform", &a); dest = (GF_Matrix2D*)a.far_ptr; if (com->tag==GF_SG_LSR_REPLACE) { if (gf_mx2d_decompose(dest, &scale, &rotate, &translate)) { gf_mx2d_init(*dest); if (inf->fieldType==SVG_TRANSFORM_SCALE) scale = *(GF_Point2D *)inf->field_ptr; else if (inf->fieldType==SVG_TRANSFORM_TRANSLATE) translate = *(GF_Point2D *)inf->field_ptr; else if (inf->fieldType==SVG_TRANSFORM_ROTATE) rotate = ((SVG_Point_Angle*)inf->field_ptr)->angle; gf_mx2d_add_scale(dest, scale.x, scale.y); gf_mx2d_add_rotation(dest, 0, 0, rotate); gf_mx2d_add_translation(dest, translate.x, translate.y); } } else { GF_Point2D *pt = (GF_Point2D *)inf->field_ptr; if (inf->fieldType==SVG_TRANSFORM_SCALE) gf_mx2d_add_scale(dest, pt->x, pt->y); else if (inf->fieldType==SVG_TRANSFORM_TRANSLATE) gf_mx2d_add_translation(dest, pt->x, pt->y); else if (inf->fieldType == SVG_TRANSFORM_ROTATE) gf_mx2d_add_rotation(dest, 0, 0, ((SVG_Point_Angle*)inf->field_ptr)->angle); } } else { if ((inf->fieldIndex==(u32) -1) && (inf->fieldType==SVG_String_datatype)) { char *str = *(SVG_String*)inf->field_ptr; if (com->tag == GF_SG_LSR_REPLACE) { GF_DOMText *t = ((SVG_Element*)com->node)->children ? (GF_DOMText*) ((SVG_Element*)com->node)->children->node :NULL; if (t && (t->sgprivate->tag==TAG_DOMText)) { if (t->textContent) free(t->textContent); t->textContent = NULL; if (str) t->textContent = strdup(str); } } else { if (str) gf_dom_add_text_node(com->node, strdup(str)); } } else if ((inf->fieldIndex==TAG_LSR_ATT_scale) || (inf->fieldIndex==TAG_LSR_ATT_translation) || (inf->fieldIndex==TAG_LSR_ATT_rotation) ) { SVG_Transform *mx; gf_svg_get_attribute_by_tag(com->node, TAG_SVG_ATT_transform, 1, 0, &a); mx = a.far_ptr; if (com->tag == GF_SG_LSR_REPLACE) { GF_Point2D scale, translate; SVG_Point_Angle rotate; if (gf_mx2d_decompose(&mx->mat, &scale, &rotate.angle, &translate)) { gf_mx2d_init(mx->mat); if (inf->fieldIndex==TAG_LSR_ATT_scale) scale = *(GF_Point2D *)inf->field_ptr; else if (inf->fieldIndex==TAG_LSR_ATT_translation) translate = *(GF_Point2D *)inf->field_ptr; else if (inf->fieldIndex==TAG_LSR_ATT_rotation) rotate = *(SVG_Point_Angle*)inf->field_ptr; gf_mx2d_add_scale(&mx->mat, scale.x, scale.y); gf_mx2d_add_rotation(&mx->mat, 0, 0, rotate.angle); gf_mx2d_add_translation(&mx->mat, translate.x, translate.y); } } else { if (inf->fieldIndex==TAG_LSR_ATT_scale) gf_mx2d_add_scale(&mx->mat, ((GF_Point2D*)inf->field_ptr)->x, ((GF_Point2D*)inf->field_ptr)->y); if (inf->fieldIndex==TAG_LSR_ATT_translation) gf_mx2d_add_translation(&mx->mat, ((GF_Point2D*)inf->field_ptr)->x, ((GF_Point2D*)inf->field_ptr)->y); if (inf->fieldIndex==TAG_LSR_ATT_rotation) gf_mx2d_add_rotation(&mx->mat, 0, 0, ((SVG_Point_Angle*)inf->field_ptr)->angle); } } else if (gf_svg_get_attribute_by_tag(com->node, inf->fieldIndex, 1, 0, &a) == GF_OK) { b = a; b.far_ptr = inf->field_ptr; if (com->tag == GF_SG_LSR_REPLACE) { gf_svg_attributes_copy(&a, &b, 0); } else { gf_svg_attributes_add(&a, &b, &a, 0); } } b = a; b.far_ptr = inf->field_ptr; } /*signal node modif*/ gf_node_changed(com->node, &a); } else if (com->fromNodeID) { GF_FieldInfo a, b; GF_Node *fromNode = gf_sg_find_node(graph, com->fromNodeID); if (!fromNode) return GF_NON_COMPLIANT_BITSTREAM; if (gf_node_get_field(fromNode, com->fromFieldIndex, &b) != GF_OK) return GF_NON_COMPLIANT_BITSTREAM; if ((inf->fieldIndex==(u32) -1) && (inf->fieldType==SVG_String_datatype)) { char *str = *(SVG_String*)inf->field_ptr; if (com->tag == GF_SG_LSR_REPLACE) { GF_DOMText *t = ((SVG_Element*)com->node)->children ? (GF_DOMText*) ((SVG_Element*)com->node)->children->node :NULL; if (t && (t->sgprivate->tag==TAG_DOMText)) { if (t->textContent) free(t->textContent); t->textContent = NULL; if (str) t->textContent = strdup(str); } } else { if (str) gf_dom_add_text_node(com->node, strdup(str)); } } else { gf_node_get_field(com->node, inf->fieldIndex, &a); if (com->tag == GF_SG_LSR_REPLACE) { e = gf_svg_attributes_copy(&a, &b, 0); } else { e = gf_svg_attributes_add(&a, &b, &a, 0); } } gf_node_changed(com->node, &a); return e; } else { return GF_NON_COMPLIANT_BITSTREAM; } break; case GF_SG_LSR_ACTIVATE: gf_node_activate(com->node); break; case GF_SG_LSR_DEACTIVATE: gf_node_deactivate(com->node); gf_node_changed(com->node, NULL); break; #endif default: return GF_NOT_SUPPORTED; } if (e) return e; if (com->scripts_to_load) { while (gf_list_count(com->scripts_to_load)) { GF_Node *script = (GF_Node *)gf_list_get(com->scripts_to_load, 0); gf_list_rem(com->scripts_to_load, 0); gf_sg_script_load(script); } gf_list_del(com->scripts_to_load); com->scripts_to_load = NULL; } return GF_OK; }
static void svg_recompute_viewport_transformation(GF_Node *node, SVGsvgStack *stack, GF_TraverseState *tr_state, SVGAllAttributes *atts) { GF_Matrix2D mx; SVG_ViewBox ext_vb, *vb; SVG_PreserveAspectRatio par; Fixed scale, vp_w, vp_h; Fixed parent_width, parent_height, doc_width, doc_height; /*canvas size negociation has already been done when attaching the scene to the compositor*/ if (atts->width && (atts->width->type==SVG_NUMBER_PERCENTAGE) ) { parent_width = gf_mulfix(tr_state->vp_size.x, atts->width->value/100); doc_width = 0; } else if (!stack->root_svg) { doc_width = parent_width = atts->width ? atts->width->value : 0; } else { parent_width = tr_state->vp_size.x; doc_width = atts->width ? atts->width->value : 0; } if (atts->height && (atts->height->type==SVG_NUMBER_PERCENTAGE) ) { parent_height = gf_mulfix(tr_state->vp_size.y, atts->height->value/100); doc_height = 0; } else if (!stack->root_svg) { doc_height = parent_height = atts->height ? atts->height->value : 0; } else { parent_height = tr_state->vp_size.y; doc_height = atts->height ? atts->height->value : 0; } stack->vp = stack->parent_vp = tr_state->vp_size; vb = atts->viewBox; gf_mx2d_init(mx); if (stack->root_svg) { const char *frag_uri = gf_scene_get_fragment_uri(node); if (frag_uri) { /*SVGView*/ if (!strncmp(frag_uri, "svgView", 7)) { if (!strncmp(frag_uri, "svgView(viewBox(", 16)) { Float x, y, w, h; sscanf(frag_uri, "svgView(viewBox(%f,%f,%f,%f))", &x, &y, &w, &h); ext_vb.x = FLT2FIX(x); ext_vb.y = FLT2FIX(y); ext_vb.width = FLT2FIX(w); ext_vb.height = FLT2FIX(h); ext_vb.is_set = 1; vb = &ext_vb; } else if (!strncmp(frag_uri, "svgView(transform(", 18)) { Bool ret = gf_svg_parse_transformlist(&mx, (char *) frag_uri+18); if (!ret) { GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Error parsing SVG View transform component: %s\n", frag_uri+18)); } } } /*fragID*/ else { GF_Node *target = gf_sg_find_node_by_name(gf_node_get_graph(node), (char *) frag_uri); if (target) { GF_Matrix2D mx; GF_TraverseState bounds_state; memset(&bounds_state, 0, sizeof(bounds_state)); bounds_state.traversing_mode = TRAVERSE_GET_BOUNDS; bounds_state.visual = tr_state->visual; bounds_state.for_node = target; bounds_state.svg_props = tr_state->svg_props; gf_mx2d_init(bounds_state.transform); gf_mx2d_init(bounds_state.mx_at_node); gf_mx_init(tr_state->visual->compositor->hit_world_to_local); gf_sc_get_nodes_bounds(node, ((GF_ParentNode *)node)->children, &bounds_state, NULL); gf_mx2d_from_mx(&mx, &tr_state->visual->compositor->hit_world_to_local); gf_mx2d_apply_rect(&mx, &bounds_state.bounds); ext_vb.x = bounds_state.bounds.x; ext_vb.y = bounds_state.bounds.y-bounds_state.bounds.height; ext_vb.width = bounds_state.bounds.width; ext_vb.height = bounds_state.bounds.height; ext_vb.is_set = 1; vb = &ext_vb; } } } } gf_mx2d_init(stack->viewbox_mx); if (!vb) { if (!doc_width || !doc_height) { gf_mx2d_copy(stack->viewbox_mx, mx); return; } /*width/height were specified in the doc, use them to compute a dummy viewbox*/ ext_vb.x = 0; ext_vb.y = 0; ext_vb.width = doc_width; ext_vb.height = doc_height; ext_vb.is_set = 1; vb = &ext_vb; } if ((vb->width<=0) || (vb->height<=0) ) { gf_mx2d_copy(stack->viewbox_mx, mx); return; } stack->vp.x = vb->width; stack->vp.y = vb->height; /*setup default*/ par.defer = 0; par.meetOrSlice = SVG_MEETORSLICE_MEET; par.align = SVG_PRESERVEASPECTRATIO_XMIDYMID; /*use parent (animation, image) viewport settings*/ if (tr_state->parent_anim_atts) { if (tr_state->parent_anim_atts->preserveAspectRatio) { if (tr_state->parent_anim_atts->preserveAspectRatio->defer) { if (atts->preserveAspectRatio) par = *atts->preserveAspectRatio; } else { par = *tr_state->parent_anim_atts->preserveAspectRatio; } } } /*use current viewport settings*/ else if (atts->preserveAspectRatio) { par = *atts->preserveAspectRatio; } if (par.meetOrSlice==SVG_MEETORSLICE_MEET) { if (gf_divfix(parent_width, vb->width) > gf_divfix(parent_height, vb->height)) { scale = gf_divfix(parent_height, vb->height); vp_w = gf_mulfix(vb->width, scale); vp_h = parent_height; } else { scale = gf_divfix(parent_width, vb->width); vp_w = parent_width; vp_h = gf_mulfix(vb->height, scale); } } else { if (gf_divfix(parent_width, vb->width) < gf_divfix(parent_height, vb->height)) { scale = gf_divfix(parent_height, vb->height); vp_w = gf_mulfix(vb->width, scale); vp_h = parent_height; } else { scale = gf_divfix(parent_width, vb->width); vp_w = parent_width; vp_h = gf_mulfix(vb->height, scale); } } if (par.align==SVG_PRESERVEASPECTRATIO_NONE) { stack->viewbox_mx.m[0] = gf_divfix(parent_width, vb->width); stack->viewbox_mx.m[4] = gf_divfix(parent_height, vb->height); stack->viewbox_mx.m[2] = - gf_muldiv(vb->x, parent_width, vb->width); stack->viewbox_mx.m[5] = - gf_muldiv(vb->y, parent_height, vb->height); } else { Fixed dx, dy; stack->viewbox_mx.m[0] = stack->viewbox_mx.m[4] = scale; stack->viewbox_mx.m[2] = - gf_mulfix(vb->x, scale); stack->viewbox_mx.m[5] = - gf_mulfix(vb->y, scale); dx = dy = 0; switch (par.align) { case SVG_PRESERVEASPECTRATIO_XMINYMIN: break; case SVG_PRESERVEASPECTRATIO_XMIDYMIN: dx = ( parent_width - vp_w) / 2; break; case SVG_PRESERVEASPECTRATIO_XMAXYMIN: dx = parent_width - vp_w; break; case SVG_PRESERVEASPECTRATIO_XMINYMID: dy = ( parent_height - vp_h) / 2; break; case SVG_PRESERVEASPECTRATIO_XMIDYMID: dx = ( parent_width - vp_w) / 2; dy = ( parent_height - vp_h) / 2; break; case SVG_PRESERVEASPECTRATIO_XMAXYMID: dx = parent_width - vp_w; dy = ( parent_height - vp_h) / 2; break; case SVG_PRESERVEASPECTRATIO_XMINYMAX: dy = parent_height - vp_h; break; case SVG_PRESERVEASPECTRATIO_XMIDYMAX: dx = (parent_width - vp_w) / 2; dy = parent_height - vp_h; break; case SVG_PRESERVEASPECTRATIO_XMAXYMAX: dx = parent_width - vp_w; dy = parent_height - vp_h; break; } gf_mx2d_add_translation(&stack->viewbox_mx, dx, dy); stack->dx = dx; stack->dy = dy; stack->vpw = vp_w; stack->vph = vp_h; /*we need a clipper*/ if (stack->root_svg && !tr_state->parent_anim_atts && (par.meetOrSlice==SVG_MEETORSLICE_SLICE)) { GF_Rect rc; rc.width = parent_width; rc.height = parent_height; if (!stack->root_svg) { rc.x = 0; rc.y = parent_height; gf_mx2d_apply_rect(&stack->viewbox_mx, &rc); } else { rc.x = dx; rc.y = dy + parent_height; } // tr_state->visual->top_clipper = gf_rect_pixelize(&rc); } } gf_mx2d_add_matrix(&stack->viewbox_mx, &mx); }
void VS2D_TexturePathIntern(VisualSurface2D *surf, GF_Path *path, GF_TextureHandler *txh, struct _drawable_context *ctx) { Fixed sS, sT; u32 tx_tile; GF_Matrix2D gf_mx2d_txt, gf_sr_texture_transform; GF_Rect rc, orig_rc; GF_Raster2D *r2d = surf->render->compositor->r2d; if (!txh) txh = ctx->h_texture; if (!txh || !txh->hwtx) return; /*this is gradient draw*/ if (txh->compute_gradient_matrix) { VS2D_DrawGradient(surf, path, txh, ctx); return; } /*setup quality even for background (since quality concerns images)*/ VS2D_SetOptions(surf->render, surf->the_surface, ctx->flags & CTX_IS_TEXT, ctx->flags & CTX_NO_ANTIALIAS); /*get original bounds*/ gf_path_get_bounds(path, &orig_rc); /*get active texture window in pixels*/ rc.width = INT2FIX(txh->width); rc.height = INT2FIX(txh->height); /*get scaling ratio so that active texture view is stretched to original bounds (std 2D shape texture mapping in MPEG4)*/ sS = orig_rc.width / txh->width; sT = orig_rc.height / txh->height; gf_mx2d_init(gf_mx2d_txt); gf_mx2d_add_scale(&gf_mx2d_txt, sS, sT); /*apply texture transform*/ get_gf_sr_texture_transform(ctx->appear, txh, &gf_sr_texture_transform, (txh == ctx->h_texture) ? 0 : 1, txh->width * sS, txh->height * sT); gf_mx2d_add_matrix(&gf_mx2d_txt, &gf_sr_texture_transform); /*move to bottom-left corner of bounds */ gf_mx2d_add_translation(&gf_mx2d_txt, (orig_rc.x), (orig_rc.y - orig_rc.height)); /*move to final coordinate system (except background which is built directly in final coord system)*/ if (!(ctx->flags & CTX_IS_BACKGROUND) ) gf_mx2d_add_matrix(&gf_mx2d_txt, &ctx->transform); /*set path transform, except for background2D node which is directly build in the final coord system*/ r2d->stencil_set_matrix(txh->hwtx, &gf_mx2d_txt); tx_tile = 0; if (txh->flags & GF_SR_TEXTURE_REPEAT_S) tx_tile |= GF_TEXTURE_REPEAT_S; if (txh->flags & GF_SR_TEXTURE_REPEAT_T) tx_tile |= GF_TEXTURE_REPEAT_T; r2d->stencil_set_tiling(txh->hwtx, (GF_TextureTiling) tx_tile); if (!(ctx->flags & CTX_IS_BACKGROUND) ) { u8 a = GF_COL_A(ctx->aspect.fill_color); if (!a) a = GF_COL_A(ctx->aspect.line_color); /*texture alpha scale is the original material transparency, NOT the one after color transform*/ r2d->stencil_set_texture_alpha(txh->hwtx, a ); r2d->stencil_set_color_matrix(txh->hwtx, ctx->col_mat); r2d->surface_set_matrix(surf->the_surface, &ctx->transform); } else { r2d->surface_set_matrix(surf->the_surface, NULL); } /*push path & draw*/ r2d->surface_set_path(surf->the_surface, path); VS2D_DoFill(surf, ctx, txh->hwtx); r2d->surface_set_path(surf->the_surface, NULL); ctx->flags |= CTX_PATH_FILLED; }
void visual_2d_texture_path_extended(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) { Fixed sS, sT; u32 tx_tile; GF_STENCIL tx_raster; GF_Matrix2D mx_texture; GF_Rect orig_rc; GF_Raster2D *raster; if (! visual->CheckAttached(visual) ) return; raster = visual->compositor->rasterizer; if (!txh) txh = ctx->aspect.fill_texture; if (!txh) return; if (!txh->tx_io) { gf_node_dirty_set(txh->owner, 0, 1); txh->needs_refresh=1; return; } /*this is gradient draw*/ if (txh->compute_gradient_matrix) { visual_2d_draw_gradient(visual, path, txh, ctx, tr_state, ext_mx, orig_bounds); return; } #ifndef GPAC_DISABLE_3D if (visual->compositor->hybrid_opengl) { visual_2d_texture_path_opengl_auto(visual, path, txh, ctx, orig_bounds, ext_mx, tr_state); return; } #endif if (txh->flags & GF_SR_TEXTURE_PRIVATE_MEDIA) { GF_Window src, dst; visual_2d_fill_path(visual, ctx, NULL, tr_state, 0); /*if texture not ready, update the size before computing output rectangles */ if (!txh->width || !txh->height) { gf_mo_get_visual_info(txh->stream, &txh->width, &txh->height, &txh->stride, &txh->pixel_ar, &txh->pixelformat, &txh->is_flipped); /*in case the node is an MPEG-4 bitmap, force stack rebuild at next frame */ gf_node_dirty_set(ctx->drawable->node, GF_SG_NODE_DIRTY, 1); } if (compositor_texture_rectangles(visual, txh, &ctx->bi->clip, &ctx->bi->unclip, &src, &dst, NULL, NULL)) { if (txh->stream && gf_mo_set_position(txh->stream, &src, &dst)) { gf_mo_get_visual_info(txh->stream, &txh->width, &txh->height, &txh->stride, &txh->pixel_ar, &txh->pixelformat, &txh->is_flipped); /*force dirty flag to get called again*/ gf_node_dirty_set(ctx->drawable->node, GF_SG_NODE_DIRTY, 1); gf_sc_next_frame_state(visual->compositor, GF_SC_DRAW_FRAME); } } return; } if (!gf_sc_texture_push_image(txh, 0, 1)) return; tx_raster = gf_sc_texture_get_stencil(txh); /*setup quality even for background (since quality concerns images)*/ visual_2d_set_options(visual->compositor, visual->raster_surface, ctx->flags & CTX_IS_TEXT, ctx->flags & CTX_NO_ANTIALIAS); /*get original bounds*/ if (orig_bounds) { orig_rc = *orig_bounds; } else { gf_path_get_bounds(path, &orig_rc); } /*get scaling ratio so that active texture view is stretched to original bounds (std 2D shape texture mapping in MPEG4)*/ sS = orig_rc.width / txh->width; sT = orig_rc.height / txh->height; gf_mx2d_init(mx_texture); gf_mx2d_add_scale(&mx_texture, sS, sT); #ifndef GPAC_DISABLE_VRML /*apply texture transform*/ if (ctx->flags & CTX_HAS_APPEARANCE) { GF_Matrix2D tex_trans; visual_2d_get_texture_transform(ctx->appear, txh, &tex_trans, (txh == ctx->aspect.fill_texture) ? 0 : 1, txh->width * sS, txh->height * sT); gf_mx2d_add_matrix(&mx_texture, &tex_trans); } #endif /*move to bottom-left corner of bounds */ gf_mx2d_add_translation(&mx_texture, (orig_rc.x), (orig_rc.y - orig_rc.height)); if (ext_mx) gf_mx2d_add_matrix(&mx_texture, ext_mx); /*move to final coordinate system (except background which is built directly in final coord system)*/ if (!(ctx->flags & CTX_IS_BACKGROUND) ) gf_mx2d_add_matrix(&mx_texture, &ctx->transform); /*set path transform*/ raster->stencil_set_matrix(tx_raster, &mx_texture); tx_tile = 0; if (txh->flags & GF_SR_TEXTURE_REPEAT_S) tx_tile |= GF_TEXTURE_REPEAT_S; if (txh->flags & GF_SR_TEXTURE_REPEAT_T) tx_tile |= GF_TEXTURE_REPEAT_T; if (ctx->flags & CTX_FLIPED_COORDS) tx_tile |= GF_TEXTURE_FLIP; raster->stencil_set_tiling(tx_raster, (GF_TextureTiling) tx_tile); if (!(ctx->flags & CTX_IS_BACKGROUND) ) { u8 a = GF_COL_A(ctx->aspect.fill_color); if (!a) a = GF_COL_A(ctx->aspect.line_color); /*texture alpha scale is the original material transparency, NOT the one after color transform*/ raster->stencil_set_alpha(tx_raster, a ); raster->stencil_set_color_matrix(tx_raster, ctx->col_mat); raster->surface_set_matrix(visual->raster_surface, &ctx->transform); } else { raster->surface_set_matrix(visual->raster_surface, NULL); } txh->flags |= GF_SR_TEXTURE_USED; /*push path & draw*/ raster->surface_set_path(visual->raster_surface, path); visual_2d_fill_path(visual, ctx, tx_raster, tr_state, 0); raster->surface_set_path(visual->raster_surface, NULL); ctx->flags |= CTX_PATH_FILLED; }
static void RenderPathLayout(GF_Node *node, void *rs, Bool is_destroy) { u32 i, count, minor, major, int_bck; Fixed length, offset, length_after_point; Bool res; ChildGroup2D *cg; GF_Matrix2D mat; GroupingNode2D *parent_bck; PathLayoutStack *gr = (PathLayoutStack *) gf_node_get_private(node); M_PathLayout *pl = (M_PathLayout *)node; RenderEffect2D *eff = (RenderEffect2D *) rs; if (is_destroy) { DeleteGroupingNode2D((GroupingNode2D *)gr); if (gr->iter) gf_path_iterator_del(gr->iter); free(gr); return; } if (!pl->geometry) return; /*only low-level primitives allowed*/ switch (gf_node_get_tag((GF_Node *) pl->geometry)) { case TAG_MPEG4_Rectangle: return; case TAG_MPEG4_Circle: return; case TAG_MPEG4_Ellipse: return; } /*store effect*/ gf_mx2d_copy(mat, eff->transform); parent_bck = eff->parent; gf_mx2d_init(eff->transform); eff->parent = NULL; /*check geom changes*/ if ((pl->geometry != gr->last_geom) || gf_node_dirty_get(pl->geometry)) { if (gr->iter) gf_path_iterator_del(gr->iter); gr->iter = NULL; int_bck = eff->trav_flags; eff->trav_flags |= GF_SR_TRAV_SWITCHED_OFF; gf_node_render(pl->geometry, eff); eff->trav_flags = int_bck; gr->last_geom = pl->geometry; } if (!gr->iter) { /*get the drawable*/ Drawable *dr = (Drawable *) gf_node_get_private( (GF_Node *) pl->geometry); /*init iteration*/ if (!dr || !dr->path) return; gr->iter = gf_path_iterator_new(dr->path); if (!gr->iter) return; } eff->parent = (GroupingNode2D*)gr; int_bck = eff->text_split_mode; eff->text_split_mode = 2; group2d_traverse((GroupingNode2D*)gr, pl->children, eff); eff->text_split_mode = int_bck; /*restore effect*/ gf_mx2d_copy(eff->transform, mat); eff->parent = parent_bck; count = gf_list_count(gr->groups); length = gf_path_iterator_get_length(gr->iter); offset = gf_mulfix(length, pl->pathOffset); major = pl->alignment.count ? pl->alignment.vals[0] : 0; minor = (pl->alignment.count==2) ? pl->alignment.vals[1] : 0; if (pl->wrapMode==1) { while (offset<0) offset += length; } for (i=0; i<count; i++) { cg = (ChildGroup2D *)gf_list_get(gr->groups, i); if (cg->original.width>length) break; /*first set our center and baseline*/ gf_mx2d_init(mat); /*major align*/ switch (major) { case 2: if (cg->is_text_group) gf_mx2d_add_translation(&mat, -1*cg->original.x - cg->original.width, 0); else gf_mx2d_add_translation(&mat, -1 * cg->original.width/2, 0); length_after_point = 0; break; case 1: length_after_point = cg->original.width/2; if (cg->is_text_group) gf_mx2d_add_translation(&mat, -1*cg->original.x - cg->original.width / 2, 0); break; default: case 0: if (cg->is_text_group) gf_mx2d_add_translation(&mat, cg->original.x, 0); else gf_mx2d_add_translation(&mat, cg->original.width/2, 0); length_after_point = cg->original.width; break; } /*if wrapping and out of path, restart*/ if ((pl->wrapMode==1) && (offset+length_after_point>=length)) { offset += length_after_point; offset -= length; i--; continue; } /*if not wrapping and not yet in path skip */ if (!pl->wrapMode && (offset+length_after_point < 0)) { child2d_render_done_complex(cg, (RenderEffect2D *)rs, NULL); goto next; } /*minor justify*/ switch (minor) { /*top alignment*/ case 3: if (cg->is_text_group) gf_mx2d_add_translation(&mat, 0, -1 * cg->ascent); else gf_mx2d_add_translation(&mat, 0, -1 * cg->original.height / 2); break; /*baseline*/ case 1: /*move to bottom align if not text*/ if (!cg->is_text_group) gf_mx2d_add_translation(&mat, 0, cg->original.height / 2); break; /*middle*/ case 2: /*if text use (asc+desc) /2 as line height since glyph height differ*/ if (cg->is_text_group) gf_mx2d_add_translation(&mat, 0, cg->descent - (cg->ascent + cg->descent) / 2); break; /*bottomline alignment*/ case 0: default: if (cg->is_text_group) gf_mx2d_add_translation(&mat, 0, cg->descent); else gf_mx2d_add_translation(&mat, 0, cg->original.height / 2); break; } res = gf_path_iterator_get_transform(gr->iter, offset, (Bool) (pl->wrapMode==2), &mat, 1, length_after_point); if (!res) break; child2d_render_done_complex(cg, (RenderEffect2D *)rs, &mat); next: if (i+1<count) { ChildGroup2D *cg_next = (ChildGroup2D *)gf_list_get(gr->groups, i+1); /*update offset according to major alignment */ switch (major) { case 2: if (cg_next->is_text_group) offset += gf_mulfix(pl->spacing, cg_next->original.x); offset += gf_mulfix(pl->spacing, cg_next->original.width); break; case 1: if (cg->is_text_group) offset += gf_mulfix(pl->spacing, cg->original.x / 2); offset += gf_mulfix(pl->spacing, cg->original.width / 2); offset += cg_next->original.width / 2; break; default: case 0: if (cg->is_text_group) offset += gf_mulfix(pl->spacing, cg->original.x); offset += gf_mulfix(pl->spacing, cg->original.width); break; } } /*wrap*/ if ((pl->wrapMode==1) && (offset>=length)) offset-=length; } /*undrawn nodes*/ for (;i<count; i++) { cg = (ChildGroup2D *)gf_list_get(gr->groups, i); child2d_render_done_complex(cg, (RenderEffect2D *)rs, NULL); } group2d_reset_children((GroupingNode2D *) gr); }
GF_Err evg_surface_fill(GF_SURFACE _this, GF_STENCIL stencil) { GF_Rect rc; GF_Matrix2D mat, st_mat; Bool restore_filter; EVGSurface *surf = (EVGSurface *)_this; EVGStencil *sten = (EVGStencil *)stencil; if (!surf || !stencil) return GF_BAD_PARAM; if (!surf->ftoutline.n_points) return GF_OK; surf->sten = sten; /*setup ft raster calllbacks*/ if (!setup_grey_callback(surf)) return GF_OK; /* surf->ftparams.gray_spans = gray_spans_stub; */ get_surface_world_matrix(surf, &mat); restore_filter = 0; /*get path frame for texture convertion */ if (sten->type != GF_STENCIL_SOLID) { rc = surf->path_bounds; gf_mx2d_apply_rect(&mat, &rc); rc.x = rc.y = 0; /*assign target frame and matrix*/ sten->frame = rc; gf_mx2d_copy(sten->pmat, surf->mat); gf_mx2d_inverse(&sten->pmat); gf_mx2d_copy(st_mat, sten->smat); gf_mx2d_init(sten->smat); switch (sten->type) { case GF_STENCIL_TEXTURE: if (! ((EVG_Texture *)sten)->pixels) return GF_BAD_PARAM; if (((EVG_Texture *)sten)->mod & GF_TEXTURE_FLIP) { if (!surf->center_coords) gf_mx2d_add_scale(&sten->smat, FIX_ONE, -FIX_ONE); } else { if (surf->center_coords) gf_mx2d_add_scale(&sten->smat, FIX_ONE, -FIX_ONE); } evg_set_texture_active(sten); gf_mx2d_add_matrix(&sten->smat, &st_mat); gf_mx2d_add_matrix(&sten->smat, &mat); gf_mx2d_inverse(&sten->smat); evg_bmp_init(sten); if (((EVG_Texture *)sten)->filter == GF_TEXTURE_FILTER_DEFAULT) { restore_filter = 1; ((EVG_Texture *)sten)->filter = surf->texture_filter; } break; case GF_STENCIL_LINEAR_GRADIENT: { EVG_LinearGradient *lin = (EVG_LinearGradient *)sten; gf_mx2d_add_matrix(&sten->smat, &st_mat); gf_mx2d_add_matrix(&sten->smat, &mat); gf_mx2d_inverse(&sten->smat); /*and finalize matrix in gradient coord system*/ gf_mx2d_add_matrix(&sten->smat, &lin->vecmat); gf_mx2d_add_scale(&sten->smat, INT2FIX(1<<EVGGRADIENTBITS), INT2FIX(1<<EVGGRADIENTBITS)); } break; case GF_STENCIL_RADIAL_GRADIENT: { EVG_RadialGradient *rad = (EVG_RadialGradient*)sten; gf_mx2d_copy(sten->smat, st_mat); gf_mx2d_add_matrix(&sten->smat, &mat); gf_mx2d_inverse(&sten->smat); gf_mx2d_add_translation(&sten->smat, -rad->center.x, -rad->center.y); gf_mx2d_add_scale(&sten->smat, gf_invfix(rad->radius.x), gf_invfix(rad->radius.y)); rad->d_f.x = gf_divfix(rad->focus.x - rad->center.x, rad->radius.x); rad->d_f.y = gf_divfix(rad->focus.y - rad->center.y, rad->radius.y); /*init*/ evg_radial_init(rad); } break; } } if (surf->useClipper) { surf->ftparams.clip_xMin = surf->clipper.x; surf->ftparams.clip_yMin = surf->clipper.y; surf->ftparams.clip_xMax = (surf->clipper.x + surf->clipper.width); surf->ftparams.clip_yMax = (surf->clipper.y + surf->clipper.height); } else { surf->ftparams.clip_xMin = 0; surf->ftparams.clip_yMin = 0; surf->ftparams.clip_xMax = (surf->width); surf->ftparams.clip_yMax = (surf->height); } /*and call the raster*/ evg_raster_render(surf->raster, &surf->ftparams); /*restore stencil matrix*/ if (sten->type != GF_STENCIL_SOLID) { gf_mx2d_copy(sten->smat, st_mat); if (restore_filter) ((EVG_Texture *)sten)->filter = GF_TEXTURE_FILTER_DEFAULT; } surf->sten = 0L; return GF_OK; }
void visual_2d_texture_path_text(GF_VisualManager *visual, DrawableContext *txt_ctx, GF_Path *path, GF_Rect *object_bounds, GF_TextureHandler *txh, GF_TraverseState *tr_state) { GF_STENCIL stencil; Fixed sS, sT; GF_Matrix2D gf_mx2d_txt; GF_Rect orig_rc; u8 alpha, r, g, b; GF_ColorMatrix cmat; GF_Raster2D *raster; if (! visual->CheckAttached(visual) ) return; raster = visual->compositor->rasterizer; stencil = gf_sc_texture_get_stencil(txh); if (!stencil) return; visual_2d_set_options(visual->compositor, visual->raster_surface, 0, 1); /*get original bounds*/ orig_rc = *object_bounds; /*get scaling ratio so that active texture view is stretched to original bounds (std 2D shape texture mapping in MPEG4)*/ sS = gf_divfix(orig_rc.width, INT2FIX(txh->width)); sT = gf_divfix(orig_rc.height, INT2FIX(txh->height)); gf_mx2d_init(gf_mx2d_txt); gf_mx2d_add_scale(&gf_mx2d_txt, sS, sT); /*move to bottom-left corner of bounds */ gf_mx2d_add_translation(&gf_mx2d_txt, (orig_rc.x), (orig_rc.y - orig_rc.height)); /*move to final coordinate system*/ gf_mx2d_add_matrix(&gf_mx2d_txt, &txt_ctx->transform); /*set path transform, except for background2D node which is directly build in the final coord system*/ raster->stencil_set_matrix(stencil, &gf_mx2d_txt); alpha = GF_COL_A(txt_ctx->aspect.fill_color); r = GF_COL_R(txt_ctx->aspect.fill_color); g = GF_COL_G(txt_ctx->aspect.fill_color); b = GF_COL_B(txt_ctx->aspect.fill_color); /*if col do a cxmatrix*/ if (!r && !g && !b) { raster->stencil_set_alpha(stencil, alpha); } else { raster->stencil_set_alpha(stencil, 0xFF); memset(cmat.m, 0, sizeof(Fixed) * 20); cmat.m[4] = INT2FIX(r)/255; cmat.m[9] = INT2FIX(g)/255; cmat.m[14] = INT2FIX(b)/255; cmat.m[18] = INT2FIX(alpha)/255; cmat.identity = 0; raster->stencil_set_color_matrix(stencil, &cmat); } raster->surface_set_matrix(visual->raster_surface, &txt_ctx->transform); txh->flags |= GF_SR_TEXTURE_USED; /*push path*/ raster->surface_set_path(visual->raster_surface, path); visual_2d_fill_path(visual, txt_ctx, stencil, tr_state, 0); raster->surface_set_path(visual->raster_surface, NULL); txt_ctx->flags |= CTX_PATH_FILLED; }
/* 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); }
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 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); }