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); }
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); }
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; }
void R2D_InitTransformMatrix2D(Render2D *sr, GF_Node *node) { Transform2DStack *stack = (Transform2DStack *)malloc(sizeof(Transform2DStack)); SetupGroupingNode2D((GroupingNode2D *)stack, sr, node); gf_mx2d_init(stack->mat); gf_node_set_private(node, stack); gf_node_set_callback_function(node, RenderTransformMatrix2D); }
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)); } }
void compositor_init_transformmatrix2d(GF_Compositor *compositor, GF_Node *node) { Transform2DStack *stack; GF_SAFEALLOC(stack, Transform2DStack); gf_mx2d_init(stack->mat); gf_node_set_private(node, stack); gf_node_set_callback_function(node, TraverseTransformMatrix2D); }
void tr_mx2d_get_matrix(GF_Node *n, GF_Matrix2D *mat) { M_TransformMatrix2D *tr = (M_TransformMatrix2D*)n; gf_mx2d_init(*mat); mat->m[0] = tr->mxx; mat->m[1] = tr->mxy; mat->m[2] = tr->tx; mat->m[3] = tr->myx; mat->m[4] = tr->myy; mat->m[5] = tr->ty; }
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; }
GF_Err evg_surface_set_matrix(GF_SURFACE _this, GF_Matrix2D *mat) { GF_Matrix2D tmp; EVGSurface *surf = (EVGSurface *)_this; if (!surf) return GF_BAD_PARAM; get_surface_world_matrix(surf, &surf->mat); if (!mat) return GF_OK; gf_mx2d_init(tmp); gf_mx2d_add_matrix(&tmp, mat); gf_mx2d_add_matrix(&tmp, &surf->mat); gf_mx2d_copy(surf->mat, tmp); return GF_OK; }
static void b2D_new_status(Background2DStack *bck, M_Background2D*back) { BackgroundStatus *status; GF_SAFEALLOC(status, BackgroundStatus); gf_mx2d_init(status->ctx.transform); status->ctx.drawable = bck->drawable; status->ctx.flags = CTX_IS_BACKGROUND; status->ctx.bi = &status->bi; status->ctx.aspect.fill_color = GF_COL_ARGB_FIXED(FIX_ONE, back->backColor.red, back->backColor.green, back->backColor.blue); status->ctx.aspect.fill_texture = &bck->txh; gf_list_add(bck->status_stack, status); }
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; } }
void compositor_init_svg_svg(GF_Compositor *compositor, GF_Node *node) { GF_Node *root; SVGsvgStack *stack; GF_SAFEALLOC(stack, SVGsvgStack); root = gf_sg_get_root_node(gf_node_get_graph(node)); stack->root_svg = (root==node) ? 1 : 0; if (stack->root_svg) { GF_SAFEALLOC(stack->svg_props, SVGPropertiesPointers); gf_svg_properties_init_pointers(stack->svg_props); } gf_mx2d_init(stack->viewbox_mx); gf_node_set_private(node, stack); gf_node_set_callback_function(node, svg_traverse_svg); }
/* 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; } } }
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); }
Bool compositor_compositetexture_handle_event(GF_Compositor *compositor, GF_Node *composite_appear, GF_Event *ev, Bool is_flush) { GF_Ray ray; Fixed dist; Bool had_text_sel=0; GF_Matrix mx; GF_TraverseState *tr_state; GF_ChildNodeItem *children, *l; Bool res; SFVec3f txcoord, loc_pt, world_pt; GF_Matrix l2w_mx, w2l_mx; CompositeTextureStack *stack; GF_Node *appear, *prev_appear; M_Appearance *ap = (M_Appearance *)composite_appear; assert(ap && ap->texture); if (ev->type > GF_EVENT_MOUSEMOVE) return 0; stack = gf_node_get_private(ap->texture); if (!stack->txh.tx_io) return 0; tr_state = NULL; children = NULL; if (!is_flush) { txcoord.x = compositor->hit_texcoords.x; txcoord.y = compositor->hit_texcoords.y; txcoord.z = 0; if (gf_sc_texture_get_transform(&stack->txh, ap->textureTransform, &mx, 1)) { /*tx coords are inverted when mapping, thus applying directly the matrix will give us the untransformed coords*/ gf_mx_apply_vec(&mx, &txcoord); while (txcoord.x<0) txcoord.x += FIX_ONE; while (txcoord.x>FIX_ONE) txcoord.x -= FIX_ONE; while (txcoord.y<0) txcoord.y += FIX_ONE; while (txcoord.y>FIX_ONE) txcoord.y -= FIX_ONE; } /*convert to tx space*/ ev->mouse.x = FIX2INT( (txcoord.x - FIX_ONE/2) * stack->visual->width + FIX_ONE/2); ev->mouse.y = FIX2INT( (txcoord.y - FIX_ONE/2) * stack->visual->height + FIX_ONE/2); GF_SAFEALLOC(tr_state, GF_TraverseState); tr_state->vrml_sensors = gf_list_new(); tr_state->visual = stack->visual; tr_state->traversing_mode = TRAVERSE_PICK; tr_state->pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(ap->texture)); tr_state->vp_size.x = INT2FIX(stack->txh.width); tr_state->vp_size.y = INT2FIX(stack->txh.height); tr_state->color_mat.identity = 1; gf_mx2d_init(tr_state->transform); #ifndef GPAC_DISABLE_3D gf_mx_init(tr_state->model_matrix); #endif /*collect sensors*/ l = children = ((M_CompositeTexture2D*)ap->texture)->children; while (l) { GF_SensorHandler *hsens = compositor_mpeg4_get_sensor_handler(l->node); if (hsens) gf_list_add(tr_state->vrml_sensors, hsens); l = l->next; } } stack->temp_sensors = compositor->sensors; stack->temp_previous_sensors = compositor->previous_sensors; compositor->sensors = stack->sensors; compositor->previous_sensors = stack->previous_sensors; ray = compositor->hit_world_ray; dist = compositor->hit_square_dist; prev_appear = compositor->prev_hit_appear; /*protect against destrucion in case of self-destroy*/ if (prev_appear) { gf_node_register(prev_appear, NULL); } compositor->prev_hit_appear = stack->prev_hit_appear; appear = compositor->hit_appear; compositor->hit_appear = NULL; /*also backup current hit state in case we hit a node in the texture but don't consume the event*/ loc_pt = compositor->hit_local_point; world_pt = compositor->hit_world_point; gf_mx_copy(l2w_mx, compositor->hit_local_to_world); gf_mx_copy(w2l_mx, compositor->hit_world_to_local); if (compositor->text_selection) had_text_sel=1; if (is_flush) { res = 0; gf_list_reset(stack->sensors); gf_sc_exec_event_vrml(compositor, ev); } else { res = visual_execute_event(stack->visual, tr_state, ev, children); } if (!had_text_sel && compositor->edited_text) { stack->visual->has_text_edit = 1; } else if (!compositor->text_selection) { stack->visual->has_text_edit = 0; } if (!res) { compositor->hit_local_point = loc_pt; gf_mx_copy(compositor->hit_local_to_world, l2w_mx); gf_mx_copy(compositor->hit_world_to_local, w2l_mx); } stack->prev_hit_appear = compositor->prev_hit_appear; if (prev_appear) { if (prev_appear->sgprivate->num_instances>1) { compositor->prev_hit_appear = prev_appear; compositor->hit_appear = appear; } else { compositor->prev_hit_appear = NULL; compositor->hit_appear = NULL; } gf_node_unregister(prev_appear, NULL); } else { compositor->prev_hit_appear = prev_appear; compositor->hit_appear = appear; } compositor->hit_world_point = world_pt; compositor->hit_world_ray = ray; compositor->hit_square_dist = dist; stack->sensors = compositor->sensors; stack->previous_sensors = compositor->previous_sensors; compositor->sensors = stack->temp_sensors; stack->temp_sensors = NULL; compositor->previous_sensors = stack->temp_previous_sensors; stack->temp_previous_sensors = NULL; if (!is_flush) { gf_list_del(tr_state->vrml_sensors); #ifndef GPAC_DISABLE_3D if (tr_state->layer3d) compositor->traverse_state->layer3d = tr_state->layer3d; #endif gf_free(tr_state); } return res; }
static void composite_update(GF_TextureHandler *txh) { s32 w, h; GF_STENCIL stencil; M_Background2D *back; GF_TraverseState *tr_state; Bool invalidate_all; u32 new_pixel_format; GF_Compositor *compositor = (GF_Compositor *)txh->compositor; CompositeTextureStack *st = (CompositeTextureStack *) gf_node_get_private(txh->owner); GF_Raster2D *raster = st->visual->compositor->rasterizer; if (st->unsupported) return; /* if (compositor->recompute_ar) { gf_node_dirty_set(txh->owner, 0, 0); return; } */ if (!compositor->rebuild_offscreen_textures && (!compositor->text_edit_changed || !st->visual->has_text_edit ) && !gf_node_dirty_get(txh->owner)) { txh->needs_refresh = 0; return; } gf_node_dirty_clear(st->txh.owner, 0); new_pixel_format = 0; back = gf_list_get(st->visual->back_stack, 0); if (back && back->isBound) new_pixel_format = GF_PIXEL_RGB_24; else new_pixel_format = GF_PIXEL_RGBA; #ifdef GPAC_USE_TINYGL /*TinyGL pixel format is fixed at compile time, we cannot override it !*/ if (st->visual->type_3d) new_pixel_format = GF_PIXEL_RGBA; #else #ifndef GPAC_DISABLE_3D /*no alpha support in offscreen rendering*/ if ( (st->visual->type_3d) && !(compositor->video_out->hw_caps & GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA)) new_pixel_format = GF_PIXEL_RGB_24; #endif /*in OpenGL_ES, only RGBA can be safelly used with glReadPixels*/ #ifdef GPAC_USE_OGL_ES new_pixel_format = GF_PIXEL_RGBA; #endif #endif /*FIXME - we assume RGB+Depth+bitshape, we should check with the video out module*/ #if defined(GF_SR_USE_DEPTH) && !defined(GPAC_DISABLE_3D) if (st->visual->type_3d && (compositor->video_out->hw_caps & GF_VIDEO_HW_HAS_DEPTH) ) new_pixel_format = GF_PIXEL_RGBDS; #endif #ifndef GPAC_DISABLE_3D if (st->visual->type_3d>1) { w = ((M_CompositeTexture3D*)txh->owner)->pixelWidth; h = ((M_CompositeTexture3D*)txh->owner)->pixelHeight; } else #endif { w = ((M_CompositeTexture2D*)txh->owner)->pixelWidth; h = ((M_CompositeTexture2D*)txh->owner)->pixelHeight; } /*internal GPAC hacks for testing color spaces*/ if (w<-1) { w = -w; if (h<0) { h = -h; if (new_pixel_format==GF_PIXEL_RGBA) { new_pixel_format=GF_PIXEL_ARGB; } else { new_pixel_format=GF_PIXEL_BGR_24; } } else { if (new_pixel_format==GF_PIXEL_RGB_24) { new_pixel_format=GF_PIXEL_RGB_32; } } } else if (h<-1) { h = -h; if (new_pixel_format==GF_PIXEL_RGB_24) { new_pixel_format=GF_PIXEL_RGB_32; } } if (w<0) w = 0; if (h<0) h = 0; if (!w || !h) { if (txh->tx_io) { #ifdef GPAC_USE_TINYGL if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx); #endif gf_sc_texture_release(txh); if (txh->data) gf_free(txh->data); txh->data = NULL; txh->width = txh->height = txh->stride = 0; } return; } invalidate_all = compositor->rebuild_offscreen_textures; /*rebuild stencil*/ if (!txh->tx_io || (w != (s32) txh->width) || ( h != (s32) txh->height) || (new_pixel_format != txh->pixelformat) ) { Bool needs_stencil = 1; if (txh->tx_io) { #ifdef GPAC_USE_TINYGL if (st->tgl_ctx) ostgl_delete_context(st->tgl_ctx); #endif gf_sc_texture_release(txh); if (txh->data) gf_free(txh->data); txh->data = NULL; } /*we don't use rect ext because of no support for texture transforms*/ if (1 #ifndef GPAC_DISABLE_3D || compositor->gl_caps.npot_texture #endif ) { st->txh.width = w; st->txh.height = h; st->sx = st->sy = FIX_ONE; } else { st->txh.width = 2; while (st->txh.width<(u32)w) st->txh.width*=2; st->txh.height = 2; while (st->txh.height<(u32)h) st->txh.height*=2; st->sx = INT2FIX(st->txh.width) / w; st->sy = INT2FIX(st->txh.height) / h; } gf_sc_texture_allocate(txh); txh->pixelformat = new_pixel_format; switch (new_pixel_format) { case GF_PIXEL_RGBA: case GF_PIXEL_ARGB: txh->stride = txh->width * 4; txh->transparent = 1; break; case GF_PIXEL_RGB_565: txh->stride = txh->width * 2; txh->transparent = 0; break; case GF_PIXEL_RGBDS: txh->stride = txh->width * 4; txh->transparent = 1; break; case GF_PIXEL_RGB_24: txh->stride = txh->width * 3; txh->transparent = 0; break; } st->visual->width = txh->width; st->visual->height = txh->height; stencil = raster->stencil_new(raster, GF_STENCIL_TEXTURE); /*TODO - add support for compositeTexture3D when root is 2D visual*/ #ifndef GPAC_DISABLE_3D if (st->visual->type_3d) { GF_Compositor *compositor = st->visual->compositor; /*figure out what to do if main visual (eg video out) is not in OpenGL ...*/ if (!compositor->visual->type_3d) { /*create an offscreen window for OpenGL rendering*/ if ((compositor->offscreen_width < st->txh.width) || (compositor->offscreen_height < st->txh.height)) { #ifndef GPAC_USE_TINYGL GF_Err e; GF_Event evt; compositor->offscreen_width = MAX(compositor->offscreen_width, st->txh.width); compositor->offscreen_height = MAX(compositor->offscreen_height, st->txh.height); evt.type = GF_EVENT_VIDEO_SETUP; evt.setup.width = compositor->offscreen_width; evt.setup.height = compositor->offscreen_height; evt.setup.back_buffer = 0; evt.setup.opengl_mode = 2; e = compositor->video_out->ProcessEvent(compositor->video_out, &evt); if (e) { gf_sc_texture_release(txh); st->unsupported = 1; return; } /*reload openGL ext*/ gf_sc_load_opengl_extensions(compositor, 1); #endif } } else { needs_stencil = 0; } } #endif if (needs_stencil) { txh->data = (char*)gf_malloc(sizeof(unsigned char) * txh->stride * txh->height); memset(txh->data, 0, sizeof(unsigned char) * txh->stride * txh->height); /*set stencil texture - we don't check error as an image could not be supported by the rasterizer but still supported by the blitter (case of RGBD/RGBDS)*/ raster->stencil_set_texture(stencil, txh->data, txh->width, txh->height, txh->stride, txh->pixelformat, txh->pixelformat, 0); #ifdef GPAC_USE_TINYGL if (st->visual->type_3d && !compositor->visual->type_3d) { st->tgl_ctx = ostgl_create_context(txh->width, txh->height, txh->transparent ? 32 : 24, &txh->data, 1); GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Creating TinyGL Offscreen context %p (%d %d - pf %s)\n", st->tgl_ctx, txh->width, txh->width, gf_4cc_to_str(txh->pixelformat))); } #endif } invalidate_all = 1; gf_sc_texture_set_stencil(txh, stencil); } if (!txh->tx_io) return; stencil = gf_sc_texture_get_stencil(txh); if (!stencil) return; #ifdef GPAC_USE_TINYGL if (st->tgl_ctx) ostgl_make_current(st->tgl_ctx, 0); #endif GF_SAFEALLOC(tr_state, GF_TraverseState); tr_state->vrml_sensors = gf_list_new(); tr_state->visual = st->visual; tr_state->invalidate_all = invalidate_all; tr_state->immediate_draw = st->visual->compositor->traverse_state->immediate_draw; gf_mx2d_init(tr_state->transform); gf_cmx_init(&tr_state->color_mat); tr_state->backgrounds = st->visual->back_stack; tr_state->viewpoints = st->visual->view_stack; tr_state->pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(st->txh.owner)); tr_state->min_hsize = INT2FIX( MIN(txh->width, txh->height) ) / 2; tr_state->vp_size.x = INT2FIX(txh->width); tr_state->vp_size.y = INT2FIX(txh->height); composite_do_bindable(st->txh.owner, tr_state, st->first); st->first = 0; GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Entering draw cycle\n")); txh->needs_refresh = visual_draw_frame(st->visual, st->txh.owner, tr_state, 0); txh->transparent = (st->visual->last_had_back==2) ? 0 : 1; if (!compositor->edited_text && st->visual->has_text_edit) st->visual->has_text_edit = 0; /*set active viewport in image coordinates top-left=(0, 0), not in BIFS*/ if (0 && gf_list_count(st->visual->view_stack)) { M_Viewport *vp = (M_Viewport *)gf_list_get(st->visual->view_stack, 0); if (vp->isBound) { SFVec2f size = vp->size; if (size.x >=0 && size.y>=0) { /*FIXME - we need tracking of VP changes*/ txh->needs_refresh = 1; } } } if (txh->needs_refresh) { #ifndef GPAC_DISABLE_3D if (st->visual->camera.is_3D) { if (st->visual->compositor->visual->type_3d) { #ifndef GPAC_USE_TINYGL gf_sc_copy_to_texture(&st->txh); #else /*in TinyGL we only need to push associated bitmap to the texture*/ gf_sc_texture_push_image(&st->txh, 0, 0); #endif } else { #ifndef GPAC_USE_TINYGL gf_sc_copy_to_stencil(&st->txh); #else if (txh->pixelformat==GF_PIXEL_RGBDS) gf_get_tinygl_depth(&st->txh); #endif } } else #endif { if (raster->stencil_texture_modified) raster->stencil_texture_modified(stencil); gf_sc_texture_set_stencil(txh, stencil); } gf_sc_invalidate(st->txh.compositor, NULL); } gf_list_del(tr_state->vrml_sensors); gf_free(tr_state); GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[CompositeTexture] Leaving draw cycle\n")); }
static void attributes_set_default_value(GF_Node *node, SVGAttribute *att) { u32 node_tag = node->sgprivate->tag; switch (att->tag) { case TAG_SVG_ATT_width: case TAG_SVG_ATT_height: if (node_tag == TAG_SVG_svg) { SVG_Length *len = att->data; len->type = SVG_NUMBER_PERCENTAGE; len->value = INT2FIX(100); } break; case TAG_SVG_ATT_x2: if (node_tag == TAG_SVG_linearGradient) { ((SVG_Number *)att->data)->value = FIX_ONE; } break; case TAG_SVG_ATT_cx: case TAG_SVG_ATT_cy: case TAG_SVG_ATT_fx: case TAG_SVG_ATT_fy: case TAG_SVG_ATT_r: if (node_tag == TAG_SVG_radialGradient) { ((SVG_Number *)att->data)->value = FIX_ONE/2; } break; case TAG_SVG_ATT_dur: if (node_tag == TAG_SVG_video || node_tag == TAG_SVG_audio || node_tag == TAG_SVG_animation) { ((SMIL_Duration *)att->data)->type = SMIL_DURATION_MEDIA; } else { /*is this correct?*/ ((SMIL_Duration *)att->data)->type = SMIL_DURATION_INDEFINITE; } break; case TAG_SVG_ATT_min: ((SMIL_Duration *)att->data)->type = SMIL_DURATION_DEFINED; break; case TAG_SVG_ATT_repeatDur: ((SMIL_Duration *)att->data)->type = SMIL_DURATION_INDEFINITE; break; case TAG_SVG_ATT_calcMode: if (node_tag == TAG_SVG_animateMotion) *((SMIL_CalcMode *)att->data) = SMIL_CALCMODE_PACED; else *((SMIL_CalcMode *)att->data) = SMIL_CALCMODE_LINEAR; break; case TAG_SVG_ATT_color: ((SVG_Paint *)att->data)->type = SVG_PAINT_COLOR; ((SVG_Paint *)att->data)->color.type = SVG_COLOR_INHERIT; break; case TAG_SVG_ATT_solid_color: case TAG_SVG_ATT_stop_color: ((SVG_Paint *)att->data)->type = SVG_PAINT_COLOR; ((SVG_Paint *)att->data)->color.type = SVG_COLOR_RGBCOLOR; break; case TAG_SVG_ATT_opacity: case TAG_SVG_ATT_solid_opacity: case TAG_SVG_ATT_stop_opacity: case TAG_SVG_ATT_audio_level: case TAG_SVG_ATT_viewport_fill_opacity: ((SVG_Number *)att->data)->value = FIX_ONE; break; case TAG_SVG_ATT_display: *((SVG_Display *)att->data) = SVG_DISPLAY_INLINE; break; case TAG_SVG_ATT_fill: case TAG_SVG_ATT_stroke: ((SVG_Paint *)att->data)->type = SVG_PAINT_INHERIT; break; case TAG_SVG_ATT_stroke_dasharray: ((SVG_StrokeDashArray *)att->data)->type = SVG_STROKEDASHARRAY_INHERIT; break; case TAG_SVG_ATT_fill_opacity: case TAG_SVG_ATT_stroke_opacity: case TAG_SVG_ATT_stroke_width: case TAG_SVG_ATT_font_size: case TAG_SVG_ATT_line_increment: case TAG_SVG_ATT_stroke_dashoffset: case TAG_SVG_ATT_stroke_miterlimit: ((SVG_Number *)att->data)->type = SVG_NUMBER_INHERIT; break; case TAG_SVG_ATT_vector_effect: *((SVG_VectorEffect *)att->data) = SVG_VECTOREFFECT_NONE; break; case TAG_SVG_ATT_fill_rule: *((SVG_FillRule *)att->data) = SVG_FILLRULE_INHERIT; break; case TAG_SVG_ATT_font_weight: *((SVG_FontWeight *)att->data) = SVG_FONTWEIGHT_INHERIT; break; case TAG_SVG_ATT_visibility: *((SVG_Visibility *)att->data) = SVG_VISIBILITY_INHERIT; break; case TAG_SVG_ATT_smil_fill: *((SMIL_Fill *)att->data) = SMIL_FILL_REMOVE; break; case TAG_XMLEV_ATT_defaultAction: *((XMLEV_DefaultAction *)att->data) = XMLEVENT_DEFAULTACTION_PERFORM; break; case TAG_SVG_ATT_zoomAndPan: *((SVG_ZoomAndPan *)att->data) = SVG_ZOOMANDPAN_MAGNIFY; break; case TAG_SVG_ATT_stroke_linecap: *(SVG_StrokeLineCap*)att->data = SVG_STROKELINECAP_INHERIT; break; case TAG_SVG_ATT_stroke_linejoin: *(SVG_StrokeLineJoin*)att->data = SVG_STROKELINEJOIN_INHERIT; break; case TAG_SVG_ATT_transform: gf_mx2d_init(((SVG_Transform*)att->data)->mat); break; /*all default=0 values (don't need init)*/ case TAG_SVG_ATT_font_family: case TAG_SVG_ATT_font_style: case TAG_SVG_ATT_text_anchor: case TAG_SVG_ATT_x: case TAG_SVG_ATT_y: break; default: GF_LOG(GF_LOG_DEBUG, GF_LOG_SCENE, ("[Scene] Cannot create default value for SVG attribute %s\n", gf_svg_get_attribute_name(node, att->tag))); } }
static void TraverseUntransform(GF_Node *node, void *rs, Bool is_destroy) { UntransformStack *stack = (UntransformStack *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *) rs; if (is_destroy) { gf_free(stack); return; } if (tr_state->traversing_mode==TRAVERSE_SORT) { if (gf_node_dirty_get(node)) { Untransform_GetNode(node, &stack->untr); /*lets place it below*/ gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); } } #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { GF_Matrix mx_model; GF_Camera backup_cam; if (!tr_state->camera) return; gf_mx_copy(mx_model, tr_state->model_matrix); gf_mx_init(tr_state->model_matrix); memcpy(&backup_cam, tr_state->camera, sizeof(GF_Camera)); camera_invalidate(tr_state->camera); tr_state->camera->is_3D=0; tr_state->camera->flags |= CAM_NO_LOOKAT; tr_state->camera->end_zoom = FIX_ONE; camera_update(tr_state->camera, NULL, 1); if (tr_state->traversing_mode == TRAVERSE_SORT) { visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_PROJECTION); visual_3d_matrix_load(tr_state->visual, tr_state->camera->projection.m); visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_MODELVIEW); visual_3d_matrix_load(tr_state->visual, tr_state->camera->modelview.m); visual_3d_set_viewport(tr_state->visual, tr_state->camera->vp); gf_node_traverse_children((GF_Node *)&stack->untr, tr_state); gf_mx_copy(tr_state->model_matrix, mx_model); memcpy(tr_state->camera, &backup_cam, sizeof(GF_Camera)); visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_PROJECTION); visual_3d_matrix_load(tr_state->visual, tr_state->camera->projection.m); visual_3d_set_matrix_mode(tr_state->visual, V3D_MATRIX_MODELVIEW); visual_3d_matrix_load(tr_state->visual, tr_state->camera->modelview.m); visual_3d_set_viewport(tr_state->visual, tr_state->camera->vp); } else if (tr_state->traversing_mode == TRAVERSE_PICK) { Fixed prev_dist = tr_state->visual->compositor->hit_square_dist; GF_Ray r = tr_state->ray; tr_state->ray.orig.x = INT2FIX(tr_state->pick_x); tr_state->ray.orig.y = INT2FIX(tr_state->pick_y); tr_state->ray.orig.z = 0; tr_state->ray.dir.x = 0; tr_state->ray.dir.y = 0; tr_state->ray.dir.z = -FIX_ONE; tr_state->visual->compositor->hit_square_dist=0; gf_node_traverse_children((GF_Node *)&stack->untr, tr_state); gf_mx_copy(tr_state->model_matrix, mx_model); memcpy(tr_state->camera, &backup_cam, sizeof(GF_Camera)); tr_state->ray = r; /*nothing picked, restore previous pick*/ if (!tr_state->visual->compositor->hit_square_dist) tr_state->visual->compositor->hit_square_dist = prev_dist; } else { gf_node_traverse_children((GF_Node *)&stack->untr, tr_state); gf_mx_copy(tr_state->model_matrix, mx_model); memcpy(tr_state->camera, &backup_cam, sizeof(GF_Camera)); } } else #endif { GF_Matrix2D mx2d_backup; gf_mx2d_copy(mx2d_backup, tr_state->transform); gf_mx2d_init(tr_state->transform); group_2d_traverse((GF_Node *)&stack->untr, (GroupingNode2D *)stack, tr_state); gf_mx2d_copy(tr_state->transform, mx2d_backup); } }
/* 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); }
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; }
static void RenderLayer2D(GF_Node *node, void *rs, Bool is_destroy) { u32 i; GF_List *prevback, *prevviews; GF_Rect clip; M_Viewport *vp; ChildGroup2D *cg; GF_Matrix2D gf_mx2d_bck; GroupingNode2D *parent_bck; DrawableContext *back_ctx; Bool bool_bck; DrawableContext *ctx; M_Background2D *back; M_Layer2D *l = (M_Layer2D *)node; Layer2DStack *l2D = (Layer2DStack *) gf_node_get_private(node); RenderEffect2D *eff; if (is_destroy) { DeleteGroupingNode2D((GroupingNode2D *)l2D); gf_list_del(l2D->backs); gf_list_del(l2D->views); free(l2D); return; } eff = (RenderEffect2D *) rs; gf_mx2d_copy(gf_mx2d_bck, eff->transform); parent_bck = eff->parent; eff->parent = (GroupingNode2D *) l2D; gf_mx2d_init(eff->transform); bool_bck = eff->draw_background; prevback = eff->back_stack; prevviews = eff->view_stack; eff->back_stack = l2D->backs; eff->view_stack = l2D->views; if (l2D->first) { /*render on back first to register with stack*/ if (l->background) { eff->draw_background = 0; gf_node_render((GF_Node*) l->background, eff); group2d_reset_children((GroupingNode2D*) l2D); eff->draw_background = 1; } vp = (M_Viewport*)l->viewport; if (vp) { gf_list_add(l2D->views, vp); if (!vp->isBound) { vp->isBound = 1; gf_node_event_out_str((GF_Node*)vp, "isBound"); } } } back = NULL; if (gf_list_count(l2D->backs) ) { back = (M_Background2D*)gf_list_get(l2D->backs, 0); if (!back->isBound) back = NULL; } vp = NULL; if (gf_list_count(l2D->views)) { vp = (M_Viewport*)gf_list_get(l2D->views, 0); if (!vp->isBound) vp = NULL; } if (!eff->is_pixel_metrics) gf_mx2d_add_scale(&eff->transform, eff->min_hsize, eff->min_hsize); l2D->clip = R2D_ClipperToPixelMetrics(eff, l->size); /*apply viewport*/ if (vp) { clip = l2D->clip; vp_setup((GF_Node *) vp, eff, &clip); } back_ctx = NULL; if (back) { /*setup back size and render*/ group2d_start_child((GroupingNode2D *)l2D); eff->draw_background = 1; ctx = b2D_GetContext(back, l2D->backs); ctx->bi->unclip = l2D->clip; ctx->bi->clip = gf_rect_pixelize(&ctx->bi->unclip); gf_mx2d_init(ctx->transform); gf_node_render((GF_Node *) back, eff); eff->draw_background = 0; /*we need a trick since we're not using a dedicated surface for layer rendering, we emulate the back context: remove previous context and insert fake one*/ if (!(eff->trav_flags & TF_RENDER_DIRECT) && (gf_list_count(l2D->groups)==1)) { ChildGroup2D *cg = (ChildGroup2D *)gf_list_get(l2D->groups, 0); back_ctx = VS2D_GetDrawableContext(eff->surface); gf_list_rem(cg->contexts, 0); gf_list_add(cg->contexts, back_ctx); back_ctx->h_texture = ctx->h_texture; back_ctx->flags = ctx->flags; back_ctx->flags &= ~CTX_IS_TRANSPARENT; back_ctx->flags |= CTX_IS_BACKGROUND; back_ctx->aspect = ctx->aspect; back_ctx->drawable = ctx->drawable; drawable_check_bounds(back_ctx, eff->surface); back_ctx->bi->clip = ctx->bi->clip; back_ctx->bi->unclip = ctx->bi->unclip; } group2d_end_child((GroupingNode2D *)l2D); } group2d_traverse((GroupingNode2D *)l2D, l->children, eff); /*restore effect*/ eff->draw_background = bool_bck; gf_mx2d_copy(eff->transform, gf_mx2d_bck); eff->parent = parent_bck; eff->back_stack = prevback; eff->view_stack = prevviews; /*check bindables*/ if (l2D->first) { Bool redraw = 0; l2D->first = 0; if (!back && gf_list_count(l2D->backs)) redraw = 1; if (!vp && gf_list_count(l2D->views) ) redraw = 1; /*we missed background or viewport (was not declared as bound during traversal, and is bound now)*/ if (redraw) { group2d_reset_children((GroupingNode2D*)l2D); gf_sr_invalidate(l2D->compositor, NULL); return; } } i=0; while ((cg = (ChildGroup2D *)gf_list_enum(l2D->groups, &i))) { child2d_render_done(cg, eff, &l2D->clip); } group2d_reset_children((GroupingNode2D*)l2D); group2d_force_bounds(eff->parent, &l2D->clip); }
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); }
/* 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 TraverseLayer2D(GF_Node *node, void *rs, Bool is_destroy) { GF_List *oldb, *oldv; GF_Node *viewport; GF_Node *back; Bool prev_layer; GF_Matrix2D backup; SFVec2f prev_vp; #ifndef GPAC_DISABLE_3D GF_Matrix mx3d; GF_List *oldf, *oldn; GF_List *node_list_backup; GF_Rect prev_clipper; Bool had_clip; #endif M_Layer2D *l = (M_Layer2D *)node; Layer2DStack *st = (Layer2DStack *) gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *) rs; if (is_destroy) { gf_list_del(st->backs); gf_list_del(st->views); group_2d_destroy(node, (GroupingNode2D*)st); gf_free(st); return; } /*layers can only be used in a 2D context*/ #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d && tr_state->camera->is_3D) return; #endif /*layer2D maintains its own stacks*/ oldb = tr_state->backgrounds; oldv = tr_state->viewpoints; tr_state->backgrounds = st->backs; tr_state->viewpoints = st->views; prev_layer = tr_state->is_layer; tr_state->is_layer = 1; #ifndef GPAC_DISABLE_3D oldf = tr_state->fogs; oldn = tr_state->navigations; tr_state->fogs = tr_state->navigations = NULL; #endif l2d_CheckBindables(node, tr_state, st->first); back = (GF_Node*)gf_list_get(st->backs, 0); viewport = (GF_Node*)gf_list_get(st->views, 0); if ((tr_state->traversing_mode == TRAVERSE_SORT) || (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS)) { /*override group bounds*/ visual_get_size_info(tr_state, &st->clip.width, &st->clip.height); /*setup bounds in local coord system*/ if (l->size.x>=0) st->clip.width = l->size.x; if (l->size.y>=0) st->clip.height = l->size.y; st->clip = gf_rect_center(st->clip.width, st->clip.height); st->bounds = st->clip; } prev_vp = tr_state->vp_size; tr_state->vp_size.x = st->clip.width; tr_state->vp_size.y = st->clip.height; switch (tr_state->traversing_mode) { case TRAVERSE_SORT: #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { tr_state->layer_clipper = compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 1); visual_3d_matrix_push(tr_state->visual); gf_mx_copy(mx3d, tr_state->model_matrix); /*setup clipping*/ visual_3d_set_clipper_2d(tr_state->visual, tr_state->layer_clipper); /*apply background BEFORE viewport*/ if (back) { tr_state->traversing_mode = TRAVERSE_BINDABLE; gf_bbox_from_rect(&tr_state->bbox, &st->clip); gf_node_traverse(back, tr_state); } /*sort all children without transform, and use current transform when flushing contexts*/ gf_mx_init(tr_state->model_matrix); /*apply viewport*/ if (viewport) { tr_state->traversing_mode = TRAVERSE_BINDABLE; tr_state->bounds = st->clip; gf_node_traverse(viewport, tr_state); visual_3d_matrix_add(tr_state->visual, tr_state->model_matrix.m); } node_list_backup = tr_state->visual->alpha_nodes_to_draw; tr_state->visual->alpha_nodes_to_draw = gf_list_new(); tr_state->traversing_mode = TRAVERSE_SORT; /*reset cull flag*/ tr_state->cull_flag = 0; group_2d_traverse(node, (GroupingNode2D *)st, tr_state); visual_3d_flush_contexts(tr_state->visual, tr_state); tr_state->traversing_mode = TRAVERSE_SORT; assert(!gf_list_count(tr_state->visual->alpha_nodes_to_draw)); gf_list_del(tr_state->visual->alpha_nodes_to_draw); tr_state->visual->alpha_nodes_to_draw = node_list_backup; visual_3d_matrix_pop(tr_state->visual); gf_mx_copy(tr_state->model_matrix, mx3d); visual_3d_reset_clipper_2d(tr_state->visual); tr_state->has_layer_clip = had_clip; if (had_clip) { tr_state->layer_clipper = prev_clipper; visual_3d_set_clipper_2d(tr_state->visual, tr_state->layer_clipper); } } else #endif { GF_IRect prev_clip; GF_Rect rc; gf_mx2d_copy(backup, tr_state->transform); prev_clip = tr_state->visual->top_clipper; rc = st->clip; /*get clipper in world coordinate*/ gf_mx2d_apply_rect(&tr_state->transform, &rc); if (viewport) { tr_state->traversing_mode = TRAVERSE_BINDABLE; tr_state->bounds = st->clip; gf_node_traverse(viewport, tr_state); #if VIEWPORT_CLIPS /*move viewport box in world coordinate*/ gf_mx2d_apply_rect(&backup, &tr_state->bounds); /*and intersect with layer clipper*/ rect_intersect(&rc, &tr_state->bounds); #endif } tr_state->visual->top_clipper = gf_rect_pixelize(&rc); gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip); tr_state->traversing_mode = TRAVERSE_SORT; if (tr_state->visual->top_clipper.width && tr_state->visual->top_clipper.height) { if (back && Bindable_GetIsBound(back) ) { DrawableContext *ctx; ctx = b2d_get_context((M_Background2D*) back, st->backs); gf_mx2d_init(ctx->transform); ctx->bi->clip = tr_state->visual->top_clipper; ctx->bi->unclip = rc; if (tr_state->immediate_draw) { tr_state->ctx = ctx; tr_state->traversing_mode = TRAVERSE_DRAW_2D; gf_node_traverse(back, tr_state); tr_state->traversing_mode = TRAVERSE_SORT; tr_state->ctx = NULL; } else { DrawableContext *back_ctx = visual_2d_get_drawable_context(tr_state->visual); gf_node_traverse(back, tr_state); back_ctx->flags = ctx->flags; back_ctx->flags &= ~CTX_IS_TRANSPARENT; back_ctx->flags |= CTX_IS_BACKGROUND; back_ctx->aspect = ctx->aspect; back_ctx->drawable = ctx->drawable; drawable_check_bounds(back_ctx, tr_state->visual); back_ctx->bi->clip = ctx->bi->clip; back_ctx->bi->unclip = ctx->bi->unclip; } /*keep track of node drawn*/ if (!(ctx->drawable->flags & DRAWABLE_REGISTERED_WITH_VISUAL) ) { struct _drawable_store *it; GF_SAFEALLOC(it, struct _drawable_store); it->drawable = ctx->drawable; if (tr_state->visual->last_prev_entry) { tr_state->visual->last_prev_entry->next = it; tr_state->visual->last_prev_entry = it; } else { tr_state->visual->prev_nodes = tr_state->visual->last_prev_entry = it; } GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layer2D] Registering new drawn node %s on visual\n", gf_node_get_class_name(it->drawable->node))); ctx->drawable->flags |= DRAWABLE_REGISTERED_WITH_VISUAL; } } group_2d_traverse(node, (GroupingNode2D *)st, tr_state); } tr_state->visual->top_clipper = prev_clip; gf_mx2d_copy(tr_state->transform, backup); } break; /*check picking - we must fall in our 2D clipper*/ case TRAVERSE_PICK: if (gf_sc_pick_in_clipper(tr_state, &st->clip)) { #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { /*apply viewport*/ if (viewport) { gf_mx_copy(mx3d, tr_state->model_matrix); tr_state->traversing_mode = TRAVERSE_BINDABLE; tr_state->bounds = st->clip; gf_node_traverse(viewport, tr_state); tr_state->traversing_mode = TRAVERSE_PICK; group_2d_traverse(node, (GroupingNode2D *)st, tr_state); gf_mx_copy(tr_state->model_matrix, mx3d); } else { group_2d_traverse(node, (GroupingNode2D *)st, tr_state); } } else #endif { if (viewport) { gf_mx2d_copy(backup, tr_state->transform); tr_state->traversing_mode = TRAVERSE_BINDABLE; tr_state->bounds = st->clip; gf_node_traverse(viewport, tr_state); tr_state->traversing_mode = TRAVERSE_PICK; group_2d_traverse(node, (GroupingNode2D *)st, tr_state); gf_mx2d_copy(tr_state->transform, backup); } else { group_2d_traverse(node, (GroupingNode2D *)st, tr_state); } } } break; case TRAVERSE_GET_BOUNDS: if (tr_state->for_node) { group_2d_traverse(node, (GroupingNode2D *)st, tr_state); } else { tr_state->bounds = st->clip; #ifndef GPAC_DISABLE_3D gf_bbox_from_rect(&tr_state->bbox, &st->clip); #endif } break; case TRAVERSE_DRAW_2D: group_2d_traverse(node, (GroupingNode2D *)st, tr_state); break; #ifndef GPAC_DISABLE_3D /*drawing a layer means drawing all sub-elements as a whole (no depth sorting with parents)*/ case TRAVERSE_DRAW_3D: assert(0); break; #endif } /*restore traversing state*/ tr_state->vp_size = prev_vp; tr_state->backgrounds = oldb; tr_state->viewpoints = oldv; tr_state->is_layer = prev_layer; #ifndef GPAC_DISABLE_3D tr_state->fogs = oldf; tr_state->navigations = oldn; #endif /*in case we missed bindables*/ if (st->first) { st->first = 0; gf_sc_invalidate(tr_state->visual->compositor, NULL); } }
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 UpdateComposite2D(GF_TextureHandler *txh) { GF_Err e; u32 i; SensorHandler *hsens; RenderEffect2D *eff; M_CompositeTexture2D *ct2D = (M_CompositeTexture2D *)txh->owner; Composite2DStack *st = (Composite2DStack *) gf_node_get_private(txh->owner); GF_Raster2D *r2d = st->surf->render->compositor->r2d; if (!gf_node_dirty_get(txh->owner)) { txh->needs_refresh = 0; return; } /*rebuild stencil*/ if (!st->surf->the_surface || !txh->hwtx || ((s32) st->width != ct2D->pixelWidth) || ( (s32) st->height != ct2D->pixelHeight) ) { if (txh->hwtx) r2d->stencil_delete(txh->hwtx); txh->hwtx = NULL; if (ct2D->pixelWidth<=0) return; if (ct2D->pixelHeight<=0) return; st->width = ct2D->pixelWidth; st->height = ct2D->pixelHeight; txh->hwtx = r2d->stencil_new(r2d, GF_STENCIL_TEXTURE); e = r2d->stencil_create_texture(txh->hwtx, st->width, st->height, GF_PIXEL_ARGB); if (e) { if (txh->hwtx) r2d->stencil_delete(txh->hwtx); txh->hwtx = NULL; } } if (!txh->hwtx) return; GF_SAFEALLOC(eff, RenderEffect2D); eff->sensors = gf_list_new(); eff->surface = st->surf; if (st->surf->render->top_effect->trav_flags & TF_RENDER_DIRECT) { eff->trav_flags = TF_RENDER_DIRECT; } gf_mx2d_init(eff->transform); gf_cmx_init(&eff->color_mat); st->surf->width = st->width; st->surf->height = st->height; eff->back_stack = st->surf->back_stack; eff->view_stack = st->surf->view_stack; eff->is_pixel_metrics = gf_sg_use_pixel_metrics(gf_node_get_graph(st->txh.owner)); eff->min_hsize = INT2FIX( MIN(st->width, st->height) ) / 2; Composite_CheckBindables(st->txh.owner, eff, st->first); st->first = 0; GF_LOG(GF_LOG_DEBUG, GF_LOG_RENDER, ("[Render 2D] Entering CompositeTexture2D Render Cycle\n")); e = VS2D_InitDraw(st->surf, eff); if (e) { effect_delete(eff); return; } /*render children*/ if (gf_node_dirty_get(st->txh.owner) & GF_SG_NODE_DIRTY) { GF_ChildNodeItem *l = ct2D->children; /*rebuild sensor list */ if (gf_list_count(st->sensors)) { gf_list_del(st->sensors); st->sensors = gf_list_new(); } while (l) { if (l->node && is_sensor_node(l->node) ) { hsens = get_sensor_handler(l->node); if (hsens) gf_list_add(st->sensors, hsens); } l = l->next; } /*if we have an active sensor at this level discard all sensors in current render context (cf VRML)*/ if (gf_list_count(st->sensors)) { effect_reset_sensors(eff); } } /*add sensor to effects*/ i=0; while ((hsens = (SensorHandler*)gf_list_enum(st->sensors, &i))) { effect_add_sensor(eff, hsens, &eff->transform); } gf_node_dirty_clear(st->txh.owner, 0); /*render*/ gf_node_render_children(st->txh.owner, eff); /*finalize draw*/ txh->needs_refresh = VS2D_TerminateDraw(st->surf, eff); st->txh.transparent = st->surf->last_had_back ? 0 : 1; /* st->txh.active_window.x = 0; st->txh.active_window.y = 0; st->txh.active_window.width = st->width; st->txh.active_window.height = st->height; */ st->txh.width = st->width; st->txh.height = st->height; /*set active viewport in image coordinates top-left=(0, 0), not in BIFS*/ if (gf_list_count(st->surf->view_stack)) { M_Viewport *vp = (M_Viewport *)gf_list_get(st->surf->view_stack, 0); if (vp->isBound) { SFVec2f size = vp->size; if (size.x >=0 && size.y>=0) { /* st->txh.active_window.width = size.x; st->txh.active_window.height = size.y; st->txh.active_window.x = (st->width - size.x) / 2; st->txh.active_window.y = (st->height - size.y) / 2; */ /*FIXME - we need tracking of VP changes*/ txh->needs_refresh = 1; } } } if (txh->needs_refresh) { if (r2d->stencil_texture_modified) r2d->stencil_texture_modified(st->txh.hwtx); gf_sr_invalidate(st->txh.compositor, NULL); } effect_delete(eff); GF_LOG(GF_LOG_DEBUG, GF_LOG_RENDER, ("[Render 2D] Leaving CompositeTexture2D Render Cycle\n")); }
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); }
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; }