static void svg2bifs_node_end(void *sax_cbck, const char *name, const char *name_space) { SVG2BIFS_Converter *converter = (SVG2BIFS_Converter *)sax_cbck; GF_Node *parent; SVGPropertiesPointers *backup_props = gf_node_get_private(converter->svg_parent); memcpy(&converter->svg_props, backup_props, sizeof(SVGPropertiesPointers)); // gf_free(backup_props); gf_node_set_private(converter->svg_parent, NULL); if (!(gf_node_get_tag(converter->svg_parent) == TAG_SVG_animateTransform)) converter->bifs_parent = gf_node_get_parent(converter->bifs_parent, 0); parent = gf_node_get_parent(converter->svg_parent, 0); gf_node_unregister(converter->svg_parent, parent); if (!parent) gf_sg_set_root_node(converter->svg_sg, NULL); converter->svg_parent = parent; converter->bifs_text_node = NULL; fprintf(stdout, "END:\t%s\t%s\n", converter->svg_parent ? gf_node_get_class_name(converter->svg_parent) : "none", converter->bifs_parent ? gf_node_get_class_name(converter->bifs_parent) : "none"); }
// HasDefParent -- checks if an ancestor of this node has an ID GF_Node * V4SceneManager::HasDefParent(GF_Node * node) { assert(node); // also checks the node itself int count = gf_node_get_parent_count(node); GF_Node * parent; // loops through all the parents and recurses for (int i=0; i<count; i++) { parent = gf_node_get_parent(node, i); const char * name = gf_node_get_name(parent); if (name) return parent; parent = HasDefParent(parent); if (parent) return parent; } return NULL; }
char *gf_term_resolve_xlink(GF_Node *node, char *the_url) { char *url; GF_Scene *scene = gf_sg_get_private(gf_node_get_graph(node)); if (!scene) return NULL; url = gf_strdup(the_url); /*apply XML:base*/ while (node) { GF_FieldInfo info; if (gf_node_get_attribute_by_tag(node, TAG_XML_ATT_base, 0, 0, &info)==GF_OK) { char *new_url = gf_url_concatenate( ((XMLRI*)info.far_ptr)->string, url); if (new_url) { gf_free(url); url = new_url; } } node = gf_node_get_parent(node, 0); } /*if this is a fragment and no XML:BASE was found, this is a fragment of the current document*/ if (url[0]=='#') return url; if (scene) { char *the_url; if (scene->redirect_xml_base) { the_url = gf_url_concatenate(scene->redirect_xml_base, url); } else { // the_url = gf_url_concatenate(is->root_od->net_service->url, url); /*the root url of a document should be "." if not specified, so that the final URL resolve happens only once at the service level*/ the_url = gf_strdup(url); } gf_free(url); return the_url; } return url; }
void gf_inline_on_modified(GF_Node *node) { u32 ODID; GF_MediaObject *mo; M_Inline *pInline = (M_Inline *) node; GF_Scene *scene = (GF_Scene *)gf_node_get_private(node); ODID = gf_mo_get_od_id(&pInline->url); if (scene) { mo = (scene->root_od) ? scene->root_od->mo : NULL; /*disconnect current inline if we're the last one using it (same as regular OD session leave/join)*/ if (mo) { Bool changed = GF_TRUE; if (ODID != GF_MEDIA_EXTERNAL_ID) { if (ODID && (ODID==scene->root_od->OD->objectDescriptorID)) changed = GF_FALSE; } else { if (gf_mo_is_same_url(mo, &pInline->url, NULL, 0) ) changed = GF_FALSE; } if (mo->num_open) { if (!changed) return; gf_scene_notify_event(scene, GF_EVENT_UNLOAD, node, NULL, GF_OK, GF_TRUE); gf_node_dirty_parents(node); gf_mo_event_target_remove_by_node(mo, node); /*reset the scene pointer as it may get destroyed*/ switch (gf_node_get_tag(node)) { case TAG_MPEG4_Inline: #ifndef GPAC_DISABLE_X3D case TAG_X3D_Inline: #endif gf_node_set_private(node, NULL); break; } mo->num_open --; if (!mo->num_open) { if (ODID == GF_MEDIA_EXTERNAL_ID) { GF_Scene *parent = scene->root_od->parentscene; /*!!! THIS WILL DESTROY THE INLINE SCENE OBJECT !!!*/ gf_odm_disconnect(scene->root_od, GF_TRUE); /*and force removal of the media object*/ if (parent) { if (gf_list_del_item(parent->scene_objects, mo)>=0) { gf_sg_vrml_mf_reset(&mo->URLs, GF_SG_VRML_MFURL); gf_mo_del(mo); } } } else { gf_term_lock_media_queue(scene->root_od->term, GF_TRUE); /*external media are completely unloaded, except addons which are only declared once */ if (!scene->root_od->addon && (scene->root_od->OD->objectDescriptorID==GF_MEDIA_EXTERNAL_ID)) { scene->root_od->action_type = GF_ODM_ACTION_DELETE; } else { scene->root_od->action_type = GF_ODM_ACTION_STOP; } if (gf_list_find(scene->root_od->term->media_queue, scene->root_od)<0) gf_list_add(scene->root_od->term->media_queue, scene->root_od); gf_term_lock_media_queue(scene->root_od->term, GF_FALSE); } } } } } /*force a redraw and load scene at next pass - we cannot load the scene now because - we can be in a JS call (eg JS mutex blocked) - locating scene objects matching the new url needs exclusive access to the MediaObject list, achieved with the term net mutex - another service may already be setting up objects (eg exclusive access to the net mutex grabbed), which can trigger event forwarding - some event forwarders may request JS context (eg access to JS mutex) In such a case we would end up in a deadlock - this needs urgent fixing ... */ if (ODID) { /*if no parent we must process the url change as we may not be traversed later on (not in the scene tree)*/ if (gf_node_get_parent(node, 0)==NULL) { gf_inline_set_scene(pInline); } else { gf_node_dirty_parents(node); } } }
Bool group_cache_compute_stats(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state, DrawableContext *first_child, Bool skip_first_child) { GF_Rect group_bounds; DrawableContext *ctx; u32 nb_segments, nb_objects; u32 alpha_pixels, opaque_pixels, area_world; u32 video_cache_max_size, cache_size, prev_cache_size; u32 i; GF_RectArray ra; /*compute stats*/ nb_objects = 0; nb_segments = 0; alpha_pixels = opaque_pixels = 0; prev_cache_size = group->cached_size; /*reset bounds*/ group_bounds.width = group_bounds.height = 0; video_cache_max_size = tr_state->visual->compositor->video_cache_max_size; /*never cache root node - this should be refined*/ if (gf_node_get_parent(node, 0) == NULL) goto group_reject; if (!group->traverse_time) goto group_reject; ra_init(&ra); ctx = first_child; if (!first_child) ctx = tr_state->visual->context; if (skip_first_child) ctx = ctx->next; /*compute properties for the sub display list*/ while (ctx && ctx->drawable) { //Fixed area; u32 alpha_comp; /*get area and compute alpha/opaque coverage*/ alpha_comp = GF_COL_A(ctx->aspect.fill_color); /*add to group area*/ gf_rect_union(&group_bounds, &ctx->bi->unclip); nb_objects++; /*no alpha*/ if ((alpha_comp==0xFF) /*no transparent texture*/ && (!ctx->aspect.fill_texture || !ctx->aspect.fill_texture->transparent) ) { ra_union_rect(&ra, &ctx->bi->clip); } nb_segments += ctx->drawable->path->n_points; ctx = ctx->next; } if ( /*TEST 1: discard visually empty groups*/ (!group_bounds.width || !group_bounds.height) || /*TEST 2: discard small groups*/ (nb_objects<MIN_OBJECTS_IN_CACHE) || /*TEST 3: low complexity group*/ (nb_segments && (nb_segments<10)) ) { ra_del(&ra); goto group_reject; } ra_refresh(&ra); opaque_pixels = 0; for (i=0; i<ra.count; i++) { opaque_pixels += ra.list[i].width * ra.list[i].height; } ra_del(&ra); /*get coverage in world coords*/ area_world = FIX2INT(group_bounds.width) * FIX2INT(group_bounds.height); /*TEST 4: discard low coverage groups in world coords (plenty of space wasted) we consider that this % of the area is actually drawn - this is of course wrong, we would need to compute each path coverage in local coords then get the ratio */ if (10*opaque_pixels < 7*area_world) goto group_reject; /*the memory size allocated for the cache - cache is drawn in final coordinate system !!*/ group_bounds.width = tr_state->visual->compositor->cache_scale * group_bounds.width / 100; group_bounds.height = tr_state->visual->compositor->cache_scale * group_bounds.height / 100; cache_size = FIX2INT(group_bounds.width) * FIX2INT(group_bounds.height) * 4 /* pixelFormat is ARGB*/; /*TEST 5: cache is less than 10x10 pixels: discard*/ if (cache_size < 400) goto group_reject; /*TEST 6: cache is larger than our allowed memory: discard*/ if (cache_size>=video_cache_max_size) { tr_state->cache_too_small = 1; goto group_reject; } /*compute the delta value for measuring the group importance for later discard (avg_time - Tcache) / (size_cache - drawable_gain) */ group->priority = INT2FIX(nb_objects*1024*group->traverse_time) / cache_size / group->nb_stats_frame; /*OK, group is a good candidate for caching*/ group->nb_objects = nb_objects; group->cached_size = cache_size; /*we're moving from non-cached to cached*/ if (!(group->flags & GROUP_IS_CACHABLE)) { group->flags |= GROUP_IS_CACHABLE; tr_state->visual->compositor->draw_next_frame = 1; /*insert the candidate and then update the list in order*/ group_cache_insert_entry(node, group, tr_state); /*keep track of this cache object for later removal*/ gf_list_add(tr_state->visual->compositor->cached_groups_queue, group); GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning cache on during stat pass for node %s - %d kb used in all caches\n", gf_node_get_log_name(node), tr_state->visual->compositor->video_cache_current_size )); } /*update memory occupation*/ else { tr_state->visual->compositor->video_cache_current_size -= prev_cache_size; tr_state->visual->compositor->video_cache_current_size += group->cached_size; if (group->cache) group->cache->force_recompute = 1; } return 1; group_reject: group->nb_objects = nb_objects; if ((group->flags & GROUP_IS_CACHABLE) || group->cache) { group->flags &= ~GROUP_IS_CACHABLE; if (group->cache) { group_cache_del(group->cache); group->cache = NULL; group->flags &= ~GROUP_IS_CACHED; } gf_list_del_item(tr_state->visual->compositor->cached_groups, group); tr_state->visual->compositor->video_cache_current_size -= cache_size; } #if 0 GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] REJECT %s\tObjects: %d\tSlope: %g\tBytes: %d\tTime: %d\n", gf_node_get_log_name(node), group->nb_objects, FIX2FLT(group->priority), group->cached_size, group->traverse_time )); GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Status (B): Max: %d\tUsed: %d\tNb Groups: %d\n", tr_state->visual->compositor->video_cache_max_size, tr_state->visual->compositor->video_cache_current_size, gf_list_count(tr_state->visual->compositor->cached_groups) )); #endif return 0; }
void compositor_init_svg_glyph(GF_Compositor *compositor, GF_Node *node) { u16 utf_name[20]; u8 *utf8; u32 len; GF_Rect rc; GF_Glyph *glyph; GF_Font *font; SVG_GlyphStack *st; SVGAllAttributes atts; GF_Node *node_font = gf_node_get_parent(node, 0); /*locate the font node*/ if (node_font) node_font = gf_node_get_parent(node, 0); if (!node_font || (gf_node_get_tag(node_font)!=TAG_SVG_font) ) return; font = gf_node_get_private(node_font); if (!font) return; gf_svg_flatten_attributes((SVG_Element*)node, &atts); if (gf_node_get_tag(node)==TAG_SVG_missing_glyph) { GF_SAFEALLOC(st, SVG_GlyphStack); goto reg_common; } /*we must have unicode specified*/ if (!atts.unicode) return; GF_SAFEALLOC(st, SVG_GlyphStack); utf8 = (u8 *) *atts.unicode; len = gf_utf8_mbstowcs(utf_name, 200, (const char **) &utf8); /*this is a single glyph*/ if (len==1) { st->glyph.utf_name = utf_name[0]; st->uni_len = 1; } else { st->glyph.utf_name = (u32) (PTR_TO_U_CAST st); st->unicode = gf_malloc(sizeof(u16)*len); st->uni_len = len; memcpy(st->unicode, utf_name, sizeof(u16)*len); } reg_common: st->glyph.ID = (u32) (PTR_TO_U_CAST st); st->font = font; st->glyph.horiz_advance = font->max_advance_h; if (atts.horiz_adv_x) st->glyph.horiz_advance = FIX2INT( gf_ceil(atts.horiz_adv_x->value) ); if (atts.d) { st->glyph.path = atts.d; gf_path_get_bounds(atts.d, &rc); st->glyph.width = FIX2INT( gf_ceil(rc.width) ); st->glyph.height = FIX2INT( gf_ceil(rc.height) ); } st->glyph.vert_advance = st->glyph.height; if (!st->glyph.vert_advance) st->glyph.vert_advance = font->max_advance_v; /*register glyph*/ if (!font->glyph) { font->glyph = &st->glyph; } else { glyph = font->glyph; while (glyph->next) glyph = glyph->next; glyph->next = &st->glyph; } gf_node_set_private(node, st); gf_node_set_callback_function(node, svg_traverse_glyph); }
void compositor_init_svg_font(GF_Compositor *compositor, GF_Node *node) { SVG_handlerElement *handler; GF_Err e; SVGAllAttributes atts; GF_Font *font; GF_Node *node_font = gf_node_get_parent(node, 0); if (!node_font) return; if (gf_node_get_tag(node_font)!=TAG_SVG_font) return; gf_svg_flatten_attributes((SVG_Element*)node, &atts); if (!atts.font_family) return; /*register font to font manager*/ GF_SAFEALLOC(font, GF_Font); e = gf_font_manager_register_font(compositor->font_manager, font); if (e) { gf_free(font); return; } font->ft_mgr = compositor->font_manager; font->get_glyphs = svg_font_get_glyphs; font->load_glyph = svg_font_load_glyph; font->udta = node_font; gf_node_set_private(node_font, font); gf_node_set_callback_function(node_font, svg_traverse_font); font->name = gf_strdup(atts.font_family->value); font->em_size = atts.units_per_em ? FIX2INT( gf_ceil(atts.units_per_em->value) ) : 1000; /*Inconsistency between SVG 1.2 and 1.1 when not specify, ascent and descent are computed based on font.vert-origin-y, WHICH DOES NOT EXIST IN Tiny 1.2 !!! We assume it to be 0. */ font->ascent = atts.ascent ? FIX2INT( gf_ceil(atts.ascent->value) ) : 0; if (!font->ascent) font->ascent = font->em_size; font->descent = atts.descent ? FIX2INT( gf_ceil(atts.descent->value) ) : 0; font->baseline = atts.alphabetic ? FIX2INT( gf_ceil(atts.alphabetic->value) ) : 0; font->line_spacing = font->em_size; font->styles = 0; if (atts.font_style) { switch (*atts.font_style) { case SVG_FONTSTYLE_ITALIC: font->styles |= GF_FONT_ITALIC; break; case SVG_FONTSTYLE_OBLIQUE: font->styles |= GF_FONT_OBLIQUE; break; } } if (atts.font_variant && (*atts.font_variant ==SVG_FONTVARIANT_SMALLCAPS)) font->styles |= GF_FONT_SMALLCAPS; if (atts.font_weight) { switch(*atts.font_weight) { case SVG_FONTWEIGHT_100: font->styles |= GF_FONT_WEIGHT_100; break; case SVG_FONTWEIGHT_LIGHTER: font->styles |= GF_FONT_WEIGHT_LIGHTER; break; case SVG_FONTWEIGHT_200: font->styles |= GF_FONT_WEIGHT_200; break; case SVG_FONTWEIGHT_300: font->styles |= GF_FONT_WEIGHT_300; break; case SVG_FONTWEIGHT_400: font->styles |= GF_FONT_WEIGHT_400; break; case SVG_FONTWEIGHT_NORMAL: font->styles |= GF_FONT_WEIGHT_NORMAL; break; case SVG_FONTWEIGHT_500: font->styles |= GF_FONT_WEIGHT_500; break; case SVG_FONTWEIGHT_600: font->styles |= GF_FONT_WEIGHT_600; break; case SVG_FONTWEIGHT_700: font->styles |= GF_FONT_WEIGHT_700; break; case SVG_FONTWEIGHT_BOLD: font->styles |= GF_FONT_WEIGHT_BOLD; break; case SVG_FONTWEIGHT_800: font->styles |= GF_FONT_WEIGHT_800; break; case SVG_FONTWEIGHT_900: font->styles |= GF_FONT_WEIGHT_900; break; case SVG_FONTWEIGHT_BOLDER: font->styles |= GF_FONT_WEIGHT_BOLDER; break; } } gf_svg_flatten_attributes((SVG_Element*)node_font, &atts); font->max_advance_h = atts.horiz_adv_x ? FIX2INT( gf_ceil(atts.horiz_adv_x->value) ) : 0; font->not_loaded = 1; /*wait for onLoad event before activating the font, otherwise we may not have all the glyphs*/ handler = gf_dom_listener_build(node_font, GF_EVENT_LOAD, 0); handler->handle_event = svg_font_on_load; gf_node_set_private((GF_Node *)handler, compositor); }