Bool svg_drawable_is_over(Drawable *drawable, Fixed x, Fixed y, DrawAspect2D *asp, GF_TraverseState *tr_state, GF_Rect *glyph_rc) { Bool check_fill, check_stroke, check_over, check_outline, check_vis, inside; GF_Rect rc; u8 ptr_evt; ptr_evt = *tr_state->svg_props->pointer_events; if (ptr_evt==SVG_POINTEREVENTS_NONE) { return 0; } if (glyph_rc) { rc = *glyph_rc; } else { gf_path_get_bounds(drawable->path, &rc); } inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0; if (ptr_evt==SVG_POINTEREVENTS_BOUNDINGBOX) return inside; check_fill = check_stroke = check_over = check_outline = check_vis = 0; /* check_vis: if set, return FALSE when visible property is not "visible" check_fill: if 1, checks whether point is over path, if 2, checks if the path is painted (even with fill-opacity=0) before check_stroke: if 1, checks whether point is over path outline, if 2, checks if the path outline is painted (even with stroke-opacity=0) before */ switch (ptr_evt) { case SVG_POINTEREVENTS_VISIBLE: check_vis = 1; check_fill = 1; check_stroke = 1; break; case SVG_POINTEREVENTS_VISIBLEFILL: check_vis = 1; check_fill = 1; break; case SVG_POINTEREVENTS_VISIBLESTROKE: check_vis = 1; check_stroke = 1; break; case SVG_POINTEREVENTS_VISIBLEPAINTED: check_vis = 1; check_fill = 2; check_stroke = 2; break; case SVG_POINTEREVENTS_FILL: check_fill = 1; break; case SVG_POINTEREVENTS_STROKE: check_stroke = 1; break; case SVG_POINTEREVENTS_ALL: check_fill = 1; check_stroke = 1; break; case SVG_POINTEREVENTS_PAINTED: check_fill = 2; check_stroke = 2; break; default: return 0; } /*!!watchout!! asp2D.width is 0 if stroke not visible due to painting properties - we must override this for picking*/ if (check_stroke==1) { asp->pen_props.width = tr_state->svg_props->stroke_width ? tr_state->svg_props->stroke_width->value : 0; } if (!asp->pen_props.width) check_stroke = 0; if (check_stroke) { /*rough estimation of stroke bounding box to avoid fetching the stroke each time*/ if (!inside) { Fixed width = asp->pen_props.width; rc.x -= width; rc.y += width; rc.width += 2*width; rc.height += 2*width; inside = ( (x >= rc.x) && (y <= rc.y) && (x <= rc.x + rc.width) && (y >= rc.y - rc.height) ) ? 1 : 0; if (!inside) return 0; } } else if (!inside) { return 0; } if (check_vis) { if (*tr_state->svg_props->visibility!=SVG_VISIBILITY_VISIBLE) return 0; } if (check_fill) { /*painted or don't care about fill*/ if ((check_fill!=2) || asp->fill_texture || asp->fill_color) { if (glyph_rc) return 1; /*point is over path*/ if (gf_path_point_over(drawable->path, x, y)) return 1; } } if (check_stroke) { StrikeInfo2D *si; /*not painted or don't care about stroke*/ if ((check_stroke!=2) || asp->line_texture || asp->line_color) { if (glyph_rc) return 1; si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, asp, tr_state->appear, NULL, 0, NULL); /*point is over outline*/ if (si && si->outline && gf_path_point_over(si->outline, x, y)) return 1; } } return 0; }
void visual_2d_draw_path_extended(GF_VisualManager *visual, GF_Path *path, DrawableContext *ctx, GF_STENCIL brush, GF_STENCIL pen, GF_TraverseState *tr_state, GF_Rect *orig_bounds, GF_Matrix2D *ext_mx, Bool is_erase) { Bool dofill, dostrike; GF_Raster2D *raster = visual->compositor->rasterizer; #ifdef SKIP_DRAW return; #endif if (! visual->CheckAttached(visual) ) return; if ((ctx->flags & CTX_PATH_FILLED) && (ctx->flags & CTX_PATH_STROKE) ) { if (visual->compositor->draw_bvol) draw_clipper(visual, ctx); return; } if (! (ctx->flags & CTX_IS_BACKGROUND) ) visual_2d_set_options(visual->compositor, visual->raster_surface, ctx->flags & CTX_IS_TEXT, ctx->flags & CTX_NO_ANTIALIAS); dofill = dostrike = 0; if (!(ctx->flags & CTX_PATH_FILLED) && (is_erase || GF_COL_A(ctx->aspect.fill_color)) ) { dofill = 1; if (!brush) { brush = visual->raster_brush; raster->stencil_set_brush_color(brush, ctx->aspect.fill_color); } } /*compute width based on transform and top_level transform*/ if (!(ctx->flags & CTX_PATH_STROKE) && ctx->aspect.pen_props.width) { dostrike = 1; } else if (!dofill) { return; } /*set path transform, except for background2D node which is directly build in the final coord system*/ raster->surface_set_matrix(visual->raster_surface, (ctx->flags & CTX_IS_BACKGROUND) ? NULL : &ctx->transform); /*fill path*/ if (dofill) { #if ADAPTATION_SIZE if ((ctx->bi->clip.width<ADAPTATION_SIZE) && (ctx->bi->clip.height<ADAPTATION_SIZE)) { raster->surface_clear(visual->raster_surface, &ctx->bi->clip, ctx->aspect.fill_color); } else #endif { /*push path*/ raster->surface_set_path(visual->raster_surface, path); visual_2d_fill_path(visual, ctx, brush, tr_state, is_erase); raster->surface_set_path(visual->raster_surface, NULL); } } if (dostrike) { #if ADAPTATION_SIZE if ((ctx->bi->clip.width<ADAPTATION_SIZE) && (ctx->bi->clip.height<ADAPTATION_SIZE)) { } else #endif { StrikeInfo2D *si; if (!pen) { pen = visual->raster_brush; raster->stencil_set_brush_color(pen, ctx->aspect.line_color); } si = drawable_get_strikeinfo(visual->compositor, ctx->drawable, &ctx->aspect, ctx->appear, path, ctx->flags, NULL); if (si && si->outline) { if (ctx->aspect.line_texture) { visual_2d_texture_path_extended(visual, si->outline, ctx->aspect.line_texture, ctx, orig_bounds, ext_mx, tr_state); } else { raster->surface_set_path(visual->raster_surface, si->outline); visual_2d_fill_path(visual, ctx, pen, tr_state, 0); } /*that's ugly, but we cannot cache path outline for IFS2D/ILS2D*/ if (path && !(ctx->flags & CTX_IS_TEXT) && (path!=ctx->drawable->path) ) { gf_path_del(si->outline); si->outline = NULL; } } // drawable_reset_path_outline(ctx->drawable); } } if (visual->compositor->draw_bvol) draw_clipper(visual, ctx); }
static void svg_drawable_traverse(GF_Node *node, void *rs, Bool is_destroy, void (*rebuild_path)(GF_Node *, Drawable *, SVGAllAttributes *), Bool is_svg_rect, Bool is_svg_path) { GF_Matrix2D backup_matrix; GF_Matrix mx_3d; DrawableContext *ctx; SVGPropertiesPointers backup_props; u32 backup_flags; Drawable *drawable = (Drawable *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVGAllAttributes all_atts; if (is_destroy) { #if USE_GF_PATH /* The path is the same as the one in the SVG node, don't delete it here */ if (is_svg_path) drawable->path = NULL; #endif drawable_node_del(node); return; } assert(tr_state->traversing_mode!=TRAVERSE_DRAW_2D); if (tr_state->traversing_mode==TRAVERSE_PICK) { svg_drawable_pick(node, drawable, tr_state); return; } /*flatten attributes and apply animations + inheritance*/ gf_svg_flatten_attributes((SVG_Element *)node, &all_atts); if (!compositor_svg_traverse_base(node, &all_atts, (GF_TraverseState *)rs, &backup_props, &backup_flags)) return; /* Recreates the path (i.e the shape) only if the node is dirty */ if (gf_node_dirty_get(node) & GF_SG_SVG_GEOMETRY_DIRTY) { /*the rebuild function is responsible for cleaning the path*/ rebuild_path(node, drawable, &all_atts); gf_node_dirty_clear(node, GF_SG_SVG_GEOMETRY_DIRTY); drawable_mark_modified(drawable, tr_state); } if (drawable->path) { if (*(tr_state->svg_props->fill_rule)==GF_PATH_FILL_ZERO_NONZERO) { if (!(drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO)) { drawable->path->flags |= GF_PATH_FILL_ZERO_NONZERO; drawable_mark_modified(drawable, tr_state); } } else { if (drawable->path->flags & GF_PATH_FILL_ZERO_NONZERO) { drawable->path->flags &= ~GF_PATH_FILL_ZERO_NONZERO; drawable_mark_modified(drawable, tr_state); } } } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { if (! compositor_svg_is_display_off(tr_state->svg_props)) { DrawAspect2D asp; gf_path_get_bounds(drawable->path, &tr_state->bounds); if (!tr_state->ignore_strike) { memset(&asp, 0, sizeof(DrawAspect2D)); drawable_get_aspect_2d_svg(node, &asp, tr_state); if (asp.pen_props.width) { StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, drawable, &asp, NULL, drawable->path, 0, NULL); if (si && si->outline) { gf_path_get_bounds(si->outline, &tr_state->bounds); } } } compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, NULL); if (!tr_state->abort_bounds_traverse) gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); gf_sc_get_nodes_bounds(node, NULL, tr_state, NULL); compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, NULL); } } else if (tr_state->traversing_mode == TRAVERSE_SORT) { /*reset our flags - this may break reuse of nodes and change-detection in dirty-rect algo */ gf_node_dirty_clear(node, 0); if (!compositor_svg_is_display_off(tr_state->svg_props) && ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN) ) { compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); ctx = drawable_init_context_svg(drawable, tr_state); if (ctx) { if (is_svg_rect) { if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->transparent) {} else if (GF_COL_A(ctx->aspect.fill_color) != 0xFF) {} else if (ctx->transform.m[1] || ctx->transform.m[3]) {} else { ctx->flags &= ~CTX_IS_TRANSPARENT; if (!ctx->aspect.pen_props.width) ctx->flags |= CTX_NO_ANTIALIAS; } } if (all_atts.pathLength && all_atts.pathLength->type==SVG_NUMBER_VALUE) ctx->aspect.pen_props.path_length = all_atts.pathLength->value; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (!drawable->mesh) { drawable->mesh = new_mesh(); if (drawable->path) mesh_from_path(drawable->mesh, drawable->path); } visual_3d_draw_from_context(ctx, tr_state); ctx->drawable = NULL; } else #endif { drawable_finalize_sort(ctx, tr_state, NULL); } } compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d); } } memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers)); tr_state->svg_flags = backup_flags; }
static void TraverseIFS2D(GF_Node *node, void *rs, Bool is_destroy) { DrawableContext *ctx; M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node; Drawable *stack = (Drawable *)gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; if (is_destroy) { drawable_node_del(node); return; } if (!ifs2D->coord) return; ifs2d_check_changes(node, stack, tr_state); switch (tr_state->traversing_mode) { case TRAVERSE_DRAW_2D: IFS2D_Draw(node, tr_state); return; #ifndef GPAC_DISABLE_3D case TRAVERSE_DRAW_3D: { DrawAspect2D asp; if (!stack->mesh) { stack->mesh = new_mesh(); mesh_new_ifs2d(stack->mesh, node); } memset(&asp, 0, sizeof(DrawAspect2D)); drawable_get_aspect_2d_mpeg4(node, &asp, tr_state); if (ifs2D->color && !GF_COL_A(asp.fill_color) ) { /*use special func to disable outline recompute and handle recompute ourselves*/ StrikeInfo2D *si = drawable_get_strikeinfo(tr_state->visual->compositor, stack, &asp, tr_state->appear, NULL, 0, tr_state); if (!si->mesh_outline) { si->mesh_outline = new_mesh(); mesh_new_ils(si->mesh_outline, ifs2D->coord, &ifs2D->coordIndex, ifs2D->color, &ifs2D->colorIndex, ifs2D->colorPerVertex, 1); } visual_3d_mesh_strike(tr_state, si->mesh_outline, asp.pen_props.width, asp.line_scale, asp.pen_props.dash); } else { visual_3d_draw_2d_with_aspect(stack, tr_state, &asp); } return; } #endif case TRAVERSE_PICK: vrml_drawable_pick(stack, tr_state); return; case TRAVERSE_GET_BOUNDS: gf_path_get_bounds(stack->path, &tr_state->bounds); return; case TRAVERSE_SORT: #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) return; #endif ctx = drawable_init_context_mpeg4(stack, tr_state); if (!ctx) return; drawable_finalize_sort(ctx, tr_state, NULL); return; } }