Beispiel #1
0
/*guarentee the tr_state->candidate has the lowest delta value*/
static void group_cache_insert_entry(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state)
{
	u32 i, count;
	GF_List *cache_candidates = tr_state->visual->compositor->cached_groups;
	GroupingNode2D *current;

	current = NULL;
	count = gf_list_count(cache_candidates);
	for (i=0; i<count; i++) {
		current = gf_list_get(cache_candidates, i);
		/*if entry's priority is higher than our group, insert our group here*/
		if (current->priority >= group->priority) {
			gf_list_insert(cache_candidates, group, i);
			break;
		}
	}
	if (i==count)
		gf_list_add(cache_candidates, group);

	tr_state->visual->compositor->video_cache_current_size += group->cached_size;
	/*log the information*/
	GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE]\tAdding object %s\tObjects: %d\tSlope: %g\tSize: %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 (KB): 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)
								));
}
Beispiel #2
0
static Bool gf_cache_remove_entry(GF_Compositor *compositor, GF_Node *node, GroupingNode2D *group)
{
	u32 bytes_remove = 0;
	GF_List *cache_candidates = compositor->cached_groups;

	/*auto mode*/
	if (!group) {
		group = gf_list_get(cache_candidates, 0);
		if (!group) return 0;
		/*remove entry*/
		gf_list_rem(cache_candidates, 0);
		node = NULL;
	} else {
		/*remove entry if present*/
		if (gf_list_del_item(cache_candidates, group)<0)
			return 0;
	}

	/*disable the caching flag of the group if it was marked as such*/
	if(group->flags & GROUP_IS_CACHABLE) {
		group->flags &= ~GROUP_IS_CACHABLE;
		/*the discarded bytes*/
		bytes_remove = group->cached_size;
	}

	/*indicates cache destruction for next frame*/
	if (group->cache && (group->flags & GROUP_IS_CACHED)) {
		group->flags &= ~GROUP_IS_CACHED;
		/*the discarded bytes*/
		bytes_remove = group->cached_size;
	}

	if (bytes_remove == 0) return 0;

	assert(compositor->video_cache_current_size >= bytes_remove);
	compositor->video_cache_current_size -= bytes_remove;

	GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Removing cache %s:\t Objects: %d\tSlope: %g\tBytes: %d\tTime: %d\n",
										gf_node_get_log_name(node),
										group->nb_objects,
										FIX2FLT(group->priority),
										group->cached_size,
										FIX2FLT(group->traverse_time)));

	GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Status (B): Max: %d\tUsed: %d\tNb Groups: %d\n",
								compositor->video_cache_max_size,
								compositor->video_cache_current_size,
								gf_list_count(compositor->cached_groups)
								));
	return 1;
}
Beispiel #3
0
/* This function is called when a modification to the node has been made (scripts, updates or events ...) */
void gf_smil_timing_modified(GF_Node *node, GF_FieldInfo *field)
{
	SMILTimingAttributesPointers *timingp = NULL;
	SMIL_Timing_RTI *rti;
	
	timingp = ((SVGTimedAnimBaseElement *)node)->timingp;
	
	if (!timingp) return;
	rti = timingp->runtime;
	if (!rti) return;

	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Modification\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
	if (rti->current_interval->begin == -1) {
		gf_smil_timing_get_next_interval(rti, 1, rti->current_interval, gf_node_get_scene_time((GF_Node*)rti->timed_elt));
	} else {
		/* we don't have the right to modify the end of an element if it's not in unresolved state */
		if (rti->current_interval->end == -1) gf_smil_timing_get_interval_end(rti, rti->current_interval);
		if (0 && rti->current_interval->end == -2) {
			/* TODO: check if the interval can be discarded if end = -2,
			   probably no, because the interval is currently running*/
			GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Wrong Interval\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
			rti->current_interval->begin = -1;
			rti->current_interval->end = -1;
			return;
		}
		
		gf_smil_timing_compute_active_duration(rti, rti->current_interval);
		gf_smil_timing_print_interval(rti, 1, rti->current_interval);
	}
	gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, gf_node_get_scene_time((GF_Node*)rti->timed_elt));

	/* mark that this element has been modified and 
	   need to be reinserted at its proper place in the list of timed elements in the scenegraph */
	gf_smil_mark_modified(rti, 0);
}
Beispiel #4
0
/* Notifies the scene time to a timed element, potentially changing its status and triggering its evaluation
   Returns:
	0 if no rendering traversal is required, 
	1 if a rendering traversal is required,
   -1 if the time node is a discard which has been deleted during this notification,
   -2 means that the timed element is waiting to begin,
   -3 means that the timed element is active but does not need further notifications (set without dur) 
             but still requires a rendering traversal */
s32 gf_smil_timing_notify_time(SMIL_Timing_RTI *rti, Double in_scene_time)
{
	s32 ret = 0;
	GF_DOM_Event evt;
	SMILTimingAttributesPointers *timingp = rti->timingp;
	Bool force_end = 0;

	if (!timingp) return 0;

	/* if the scene time is the same as it was during the previous notification, it means that the 
	   animations are paused and we don't need to evaluate it again unless the force_reevaluation flag is set */
	if ((rti->scene_time == in_scene_time) && (rti->force_reevaluation == 0)) return 0;
	if (!rti->paused) rti->scene_time = in_scene_time;
	rti->force_reevaluation = 0;

	/* for fraction events, in all cases we indicate that the scene needs redraw */
	if (rti->evaluate_status == SMIL_TIMING_EVAL_FRACTION) 
		return 1;

	if (rti->evaluate_status == SMIL_TIMING_EVAL_DISCARD) {
		/* TODO: FIX ME discarding should send a begin event ? */
		/* Since the discard can only be evaluated once, it unregisters itself 
		   from the list of timed elements to be notified, so for this special case 
		   we return -1 when the discard has actually been executed */
		if (gf_smil_discard(rti, FLT2FIX(rti->scene_time))) return -1;
		else return 0;
	}

	gf_node_register(rti->timed_elt, NULL);

waiting_to_begin:
	if (rti->status == SMIL_STATUS_WAITING_TO_BEGIN) {
		if (rti->current_interval->begin != -1 && rti->scene_time >= rti->current_interval->begin) {			
			/* if there is a computed interval with a definite begin value 
			   and if that value is lesser than the scene time, then the animation becomes active */
			GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Activating\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
			rti->status = SMIL_STATUS_ACTIVE;

			if (rti->timed_elt->sgprivate->tag==TAG_LSR_conditional) {
				SVG_Element *e = (SVG_Element *)rti->timed_elt;
				/*activate conditional*/
				if (e->children) gf_node_traverse(e->children->node, NULL);
				rti->status = SMIL_STATUS_DONE;
			} else {
				gf_smil_reorder_anim(rti);
			}

			memset(&evt, 0, sizeof(evt));
			evt.type = GF_EVENT_BEGIN_EVENT;
			evt.smil_event_time = rti->current_interval->begin;
			gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);				
		} else {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Evaluating (Not starting)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
			ret = -2;
			goto exit;
		}
	}

	if (rti->status == SMIL_STATUS_ACTIVE) {
		u32 cur_id;

		if (rti->current_interval->active_duration >= 0 
			&& rti->scene_time >= (rti->current_interval->begin + rti->current_interval->active_duration)) {
force_end:
			GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Stopping \n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));

			rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, NULL);
			ret = rti->postpone;

			if (timingp->fill && *timingp->fill == SMIL_FILL_FREEZE) {
				rti->status = SMIL_STATUS_FROZEN;
				rti->evaluate_status = SMIL_TIMING_EVAL_FREEZE;
				GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Preparing to freeze\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
				if (!rti->postpone) {
					rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
				}
			} else {
				rti->status = SMIL_STATUS_DONE;
				rti->evaluate_status = SMIL_TIMING_EVAL_REMOVE;
				GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Preparing to remove\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
				if (!rti->postpone) {
					rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
				}
			}

			memset(&evt, 0, sizeof(evt));
			evt.type = GF_EVENT_END_EVENT;
			/* WARNING: begin + active_duration may be greater than 'now' because of force_end cases */
			evt.smil_event_time = rti->current_interval->begin + rti->current_interval->active_duration;
			gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);

		} else { /* the animation is still active */

			if (!timingp->restart || *timingp->restart == SMIL_RESTART_ALWAYS) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Checking for restart (always)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
			
				if (rti->next_interval->begin != -1 && rti->next_interval->begin < rti->scene_time) {
					*rti->current_interval = *rti->next_interval;
					gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->scene_time);

					/* mark that this element has been modified and 
					   need to be reinserted at its proper place in the list of timed elements in the scenegraph */
					gf_smil_mark_modified(rti, 0);

					/* if this is animation, reinserting the animation in the list of animations 
				       that targets this attribute, so that it is the last one */
					gf_smil_reorder_anim(rti);

					memset(&evt, 0, sizeof(evt));
					evt.type = GF_EVENT_BEGIN_EVENT;
					evt.smil_event_time = rti->current_interval->begin;
					gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);				
				} 
			}

			ret = rti->postpone;
			
			cur_id = rti->current_interval->nb_iterations;
			rti->normalized_simple_time = gf_smil_timing_get_normalized_simple_time(rti, rti->scene_time, &force_end);
			if (force_end) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Forcing end (fill or remove)\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
				goto force_end;
			}
			if (cur_id < rti->current_interval->nb_iterations) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_INTERACT, ("[SMIL Timing   ] Time %f - Timed element %s - Preparing to repeat\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
				memset(&evt, 0, sizeof(evt));
				evt.type = GF_EVENT_REPEAT_EVENT;
				evt.smil_event_time = rti->current_interval->begin + rti->current_interval->nb_iterations*rti->current_interval->simple_duration;
				evt.detail = rti->current_interval->nb_iterations;
				gf_dom_event_fire((GF_Node *)rti->timed_elt, &evt);

				rti->evaluate_status = SMIL_TIMING_EVAL_REPEAT;		
			} else {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Preparing to update\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
				rti->evaluate_status = SMIL_TIMING_EVAL_UPDATE;
			}

			if (!rti->postpone) {
				rti->evaluate(rti, rti->normalized_simple_time, rti->evaluate_status);
			}

			/* special case for animations with unspecified simpleDur (not with media timed elements)
			   we need to indicate that this anim does not need to be notified anymore and that 
			   it does not require tree traversal */
			if (gf_svg_is_animation_tag(rti->timed_elt->sgprivate->tag)
				&& (rti->current_interval->simple_duration==-1) 
				&& (rti->current_interval->active_duration==-1) 
			) {
				/*GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph;
				while (sg->parent_scene) sg = sg->parent_scene;
				gf_list_del_item(sg->smil_timed_elements, rti);
				ret = -3;*/
				ret = 1;
			} 
		}
	}

	if ((rti->status == SMIL_STATUS_DONE) || (rti->status == SMIL_STATUS_FROZEN)) {
		if (!timingp->restart || *timingp->restart != SMIL_RESTART_NEVER) { 
			/* Check changes in begin or end attributes */
			GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Checking for restart when not active\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
			if (rti->next_interval->begin != -1) {
				Bool restart_timing = 0;
				/*next interval is right now*/
				if (rti->next_interval->begin == rti->current_interval->begin+rti->current_interval->active_duration)
					restart_timing = 1;

				/*switch intervals*/
				if (rti->next_interval->begin >= rti->current_interval->begin+rti->current_interval->active_duration) { 
					*rti->current_interval = *rti->next_interval;
				
					gf_smil_timing_print_interval(rti, 1, rti->current_interval);
					gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->scene_time);

					/* mark that this element has been modified and 
					   need to be reinserted at its proper place in the list of timed elements in the scenegraph */
					gf_smil_mark_modified(rti, 0);
				} else {
					rti->next_interval->begin = -1;
				}

				/*if chaining to new interval, go to wait_for begin right now*/
				if (restart_timing) {
					rti->status = SMIL_STATUS_WAITING_TO_BEGIN;
					rti->evaluate_status = SMIL_TIMING_EVAL_NONE;
					GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Returning to eval none status\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
					ret = 0;
					goto waiting_to_begin;
				}
				/*otherwise move state to waiting for begin for next smil_timing evaluation, but
				don't change evaluate status for next anim evaluation*/
				else {
					rti->status = SMIL_STATUS_WAITING_TO_BEGIN;
				}
			} else {
				/*??? what is this ???*/
				//ret = 0;
			}
		} else if ((rti->status == SMIL_STATUS_DONE) && 
			        timingp->restart && (*timingp->restart == SMIL_RESTART_NEVER)) {
			/* the timed element is done and cannot restart, we don't need to evaluate it anymore */
			GF_SceneGraph * sg = rti->timed_elt->sgprivate->scenegraph;
			while (sg->parent_scene) sg = sg->parent_scene;
			gf_list_del_item(sg->smil_timed_elements, rti);
			ret = -1;
		}
	}

exit:
	gf_node_unregister(rti->timed_elt, NULL);
	return ret;
}
Beispiel #5
0
/* evaluation function for the discard element 
   returns 1 if the discard was executed
           0 otherwise
*/
static Bool gf_smil_discard(SMIL_Timing_RTI *rti, Fixed scene_time)
{
	u32 nb_inst;
	SMIL_Time *begin;
	SVGTimedAnimBaseElement *tb = (SVGTimedAnimBaseElement *)rti->timed_elt;
	SMILTimingAttributesPointers *timingp = (SMILTimingAttributesPointers *)rti->timingp;
	GF_Node *target;

	if (!timingp) return 0;
	
	target = tb->xlinkp->href ? tb->xlinkp->href->target : NULL;

	begin = (timingp->begin ? (SMIL_Time *)gf_list_get(*timingp->begin, 0) : NULL);

	if (!begin) return 0;
	if (!GF_SMIL_TIME_IS_CLOCK(begin->type) ) return 0;

	if (begin->clock > scene_time) return 0;

	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SVG Composer] discarding element %s at time %f\n", target ? gf_node_get_log_name(target) : "None", scene_time));

	gf_smil_mark_modified(rti, 1);
	
	/*this takes care of cases where discard is a child of its target*/
	gf_node_register(rti->timed_elt, NULL);
	nb_inst = gf_node_get_num_instances(rti->timed_elt);
	if (target) gf_node_replace(target, NULL, 0);
	if (nb_inst == gf_node_get_num_instances(rti->timed_elt)) {
		gf_node_unregister(rti->timed_elt, NULL);
		/*after this the stack may be free'd*/
		gf_node_replace(rti->timed_elt, NULL, 0);
	} else {
		gf_node_unregister(rti->timed_elt, NULL);
	}
	return 1;
}
Beispiel #6
0
void gf_smil_timing_delete_runtime_info(GF_Node *timed_elt, SMIL_Timing_RTI *rti)
{
	GF_SceneGraph *sg;

	if (!rti || !timed_elt) return;

	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Destruction\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
	gf_free(rti->current_interval);
	gf_free(rti->next_interval);

	/* we inform the rootmost scene graph that this node will not need notification of the scene time anymore */
	sg = timed_elt->sgprivate->scenegraph;
	while (sg->parent_scene) sg = sg->parent_scene;
	gf_list_del_item(sg->smil_timed_elements, rti);
	gf_list_del_item(sg->modified_smil_timed_elements, rti);

	/*remove all associated listeners*/
	if (rti->timingp->begin) gf_smil_timing_reset_time_list(* rti->timingp->begin);
	if (rti->timingp->end) gf_smil_timing_reset_time_list(* rti->timingp->end);
	
	gf_free(rti);
}
Beispiel #7
0
static void gf_smil_timing_print_interval(SMIL_Timing_RTI *rti, Bool current, SMIL_Interval *interval)
{
	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - ", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, (current ? "Current " : "   Next "));
	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("Interval - "));
	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("begin: %.2f", interval->begin));
	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, (" - end: %.2f", interval->end));
	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, (" - simple dur: %.2f - active dur: %.2f\n",interval->simple_duration, interval->active_duration));
}
Beispiel #8
0
/* Attributes from the timed elements are not easy to use during runtime, 
   the runtime info is a set of easy to use structures. 
   This function initializes them (intervals, status ...) 
   and registers the element with the scenegraph */
GF_EXPORT
void gf_smil_timing_init_runtime_info(GF_Node *timed_elt)
{
	GF_SceneGraph *sg;
	SMIL_Timing_RTI *rti;
	SMILTimingAttributesPointers *timingp = NULL;
	u32 tag = gf_node_get_tag(timed_elt);
	SVGAllAttributes all_atts;
	SVGTimedAnimBaseElement *e = (SVGTimedAnimBaseElement *)timed_elt;

	gf_svg_flatten_attributes((SVG_Element *)e, &all_atts);
	e->timingp = gf_malloc(sizeof(SMILTimingAttributesPointers));
	e->timingp->begin		= all_atts.begin;
	e->timingp->clipBegin	= all_atts.clipBegin;
	e->timingp->clipEnd		= all_atts.clipEnd;
	e->timingp->dur			= all_atts.dur;
	e->timingp->end			= all_atts.end;
	e->timingp->fill		= all_atts.smil_fill;
	e->timingp->max			= all_atts.max;
	e->timingp->min			= all_atts.min;
	e->timingp->repeatCount = all_atts.repeatCount;
	e->timingp->repeatDur	= all_atts.repeatDur;
	e->timingp->restart		= all_atts.restart;
	timingp = e->timingp;
	if (!timingp) return;

	if (tag == TAG_SVG_audio || tag == TAG_SVG_video) {
		/* if the dur attribute is not set, then it should be set to media 
		   as this is the default for media elements see 
		   http://www.w3.org/TR/2005/REC-SMIL2-20051213/smil-timing.html#Timing-DurValueSemantics
		   "For simple media elements that specify continuous media (i.e. media with an inherent notion of time), 
		   the implicit duration is the intrinsic duration of the media itself - e.g. video and audio files 
		   have a defined duration."
		TODO: Check if this should work with the animation element */
		if (!e->timingp->dur) {
			GF_FieldInfo info;
			gf_node_get_attribute_by_tag((GF_Node *)e, TAG_SVG_ATT_dur, 1, 0, &info);
			e->timingp->dur = (SMIL_Duration *)info.far_ptr;
			e->timingp->dur->type = SMIL_DURATION_MEDIA;
		}
	}

	GF_SAFEALLOC(rti, SMIL_Timing_RTI)
	timingp->runtime = rti;
	rti->timed_elt = timed_elt;
	GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Initialization\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));

	rti->timingp = timingp;
	rti->status = SMIL_STATUS_WAITING_TO_BEGIN;
	rti->evaluate_status = SMIL_TIMING_EVAL_NONE;	
	rti->evaluate = gf_smil_timing_null_timed_function;
	rti->scene_time = -1;
	rti->force_reevaluation = 0;
	rti->media_duration = -1;

	GF_SAFEALLOC(rti->current_interval, SMIL_Interval);
	gf_smil_timing_get_first_interval(rti);
	GF_SAFEALLOC(rti->next_interval, SMIL_Interval);
	gf_smil_timing_get_next_interval(rti, 0, rti->next_interval, rti->current_interval->begin);

	/* Now that the runtime info for this timed element is initialized, we can tell the scene graph that it can start
	   notifying the scene time to this element. Because of the 'animation' element, we can have many scene graphs
	   sharing the same scene time, we therefore add this timed element to the rootmost scene graph. */
	sg = timed_elt->sgprivate->scenegraph;
	while (sg->parent_scene) sg = sg->parent_scene;
	gf_smil_timing_add_to_sg(sg, rti);
}
Beispiel #9
0
static Bool gf_smil_timing_get_next_interval(SMIL_Timing_RTI *rti, Bool current, SMIL_Interval *interval, Double scene_time)
{
	u32 i, count;

	memset(interval, 0, sizeof(SMIL_Interval));
	interval->begin = -1;
	
	count = (rti->timingp->begin ? gf_list_count(*rti->timingp->begin) : 0);
	for (i = 0; i < count; i ++) {
		SMIL_Time *begin = (SMIL_Time*)gf_list_get(*rti->timingp->begin, i);
		if (GF_SMIL_TIME_IS_CLOCK(begin->type)) {
			if (rti->current_interval->begin != -1 && begin->clock <= rti->current_interval->begin) continue;
//			if (rti->current_interval->begin == -1 || begin->clock <= scene_time) {
				interval->begin = begin->clock;
				break;
//			}
		}
	}
	if (interval->begin != -1) {
		gf_smil_timing_get_interval_end(rti, interval);
		if (interval->end == -2) {
			/* this is a wrong interval see first animation in animate-elem-201-t.svg */
			GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Wrong Interval\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
			interval->begin = -1;
			interval->end = -1;
			return 0;
		}
		gf_smil_timing_compute_active_duration(rti, interval);
		gf_smil_timing_print_interval(rti, current, interval);
		return 1;
	} else {
		return 0;
	}
}
Beispiel #10
0
static void gf_smil_timing_get_first_interval(SMIL_Timing_RTI *rti)
{
	u32 i, count;
	memset(rti->current_interval, 0, sizeof(SMIL_Interval));
	rti->current_interval->begin = -1;
	count = (rti->timingp->begin ? gf_list_count(*rti->timingp->begin) : 0);
	for (i = 0; i < count; i ++) {
		SMIL_Time *begin = (SMIL_Time*)gf_list_get(*rti->timingp->begin, i);
		if (GF_SMIL_TIME_IS_CLOCK(begin->type)) {
			rti->current_interval->begin = begin->clock;
			break;
		}
	}
	/*In SVG, if no 'begin' is specified, the default timing of the time container 
	is equivalent to an offset value of '0'.*/
	if (rti->current_interval->begin == -1 && count == 0) {
		/* except for LASeR Conditional element*/
		if (rti->timed_elt->sgprivate->tag != TAG_LSR_conditional) {
			rti->current_interval->begin = 0;
		} else {
			return;
		}
	}
	
	/* this is the first time we check the interval */
	gf_smil_timing_get_interval_end(rti, rti->current_interval);
	if (0 && rti->current_interval->end == -2) {
		/* TODO: check if the interval can be discarded (i.e. if end is specified with an invalid end value (return -2)),
		   probably yes, but next time we call the evaluation of interval, we should call get_first_interval */
		GF_LOG(GF_LOG_DEBUG, GF_LOG_SMIL, ("[SMIL Timing   ] Time %f - Timed element %s - Wrong Interval\n", gf_node_get_scene_time((GF_Node *)rti->timed_elt), gf_node_get_log_name((GF_Node *)rti->timed_elt)));
		rti->current_interval->begin = -1;
		rti->current_interval->end = -1;
		return;
	}

	gf_smil_timing_compute_active_duration(rti, rti->current_interval);
	gf_smil_timing_print_interval(rti, 1, rti->current_interval);
}
Beispiel #11
0
void svg_drawable_pick(GF_Node *node, Drawable *drawable, GF_TraverseState *tr_state)
{
	DrawAspect2D asp;
	GF_Matrix2D inv_2d;
	Fixed x, y;
	Bool picked = 0;
	GF_Compositor *compositor = tr_state->visual->compositor;
	SVGPropertiesPointers backup_props;
	GF_Matrix2D backup_matrix;
	GF_Matrix mx_3d;
	SVGAllAttributes all_atts;

	if (!drawable->path) return;

	gf_svg_flatten_attributes((SVG_Element *)node, &all_atts);

	memcpy(&backup_props, tr_state->svg_props, sizeof(SVGPropertiesPointers));
	gf_svg_apply_inheritance(&all_atts, tr_state->svg_props);
	if (compositor_svg_is_display_off(tr_state->svg_props)) return;

	compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d);

	memset(&asp, 0, sizeof(DrawAspect2D));
	drawable_get_aspect_2d_svg(node, &asp, tr_state);

#ifndef GPAC_DISABLE_3D
	if (tr_state->visual->type_3d) {
		svg_drawable_3d_pick(drawable, tr_state, &asp);
		compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
		memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
		return;
	} 
#endif
	gf_mx2d_copy(inv_2d, tr_state->transform);
	gf_mx2d_inverse(&inv_2d);
	x = tr_state->ray.orig.x;
	y = tr_state->ray.orig.y;
	gf_mx2d_apply_coords(&inv_2d, &x, &y);

	picked = svg_drawable_is_over(drawable, x, y, &asp, tr_state, NULL);

	if (picked) {
		u32 count, i;
		compositor->hit_local_point.x = x;
		compositor->hit_local_point.y = y;
		compositor->hit_local_point.z = 0;

		gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform);
		gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d);

		compositor->hit_node = drawable->node;
		compositor->hit_use_dom_events = 1;
		compositor->hit_normal.x = compositor->hit_normal.y = 0; compositor->hit_normal.z = FIX_ONE;
		compositor->hit_texcoords.x = gf_divfix(x, drawable->path->bbox.width) + FIX_ONE/2;
		compositor->hit_texcoords.y = gf_divfix(y, drawable->path->bbox.height) + FIX_ONE/2;
		svg_clone_use_stack(compositor, tr_state);
		/*not use in SVG patterns*/
		compositor->hit_appear = NULL;

		/*also stack any VRML sensors present at the current level. If the event is not catched
		by a listener in the SVG tree, the event will be forwarded to the VRML tree*/
		gf_list_reset(tr_state->visual->compositor->sensors);
		count = gf_list_count(tr_state->vrml_sensors);
		for (i=0; i<count; i++) {
			gf_list_add(tr_state->visual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
		}

		GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[SVG Picking] node %s is under mouse - hit %g %g 0\n", gf_node_get_log_name(drawable->node), FIX2FLT(x), FIX2FLT(y)));
	}

	compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx_3d);
	memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
}
Beispiel #12
0
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;
}
Beispiel #13
0
Bool group_2d_cache_traverse(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state)
{
	Bool is_dirty = gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY;
	Bool zoom_changed = tr_state->visual->compositor->zoom_changed;
	Bool needs_recompute = 0;

	/*we are currently in a group cache, regular traversing*/
	if (tr_state->in_group_cache) return 0;

	/*draw mode*/
	if (tr_state->traversing_mode == TRAVERSE_DRAW_2D) {
		/*shall never happen*/
		assert(group->cache);
		/*draw it*/
		group_cache_draw(group->cache, tr_state);
		return 1;
	}
	/*other modes than sorting, use regular traversing*/
	if (tr_state->traversing_mode != TRAVERSE_SORT) return 0;

	/*this is not an offscreen group*/
	if (!(group->flags & GROUP_IS_CACHED) ) {
		Bool cache_on = 0;

		/*group cache has been turned on in the previous frame*/
		if (!is_dirty && (group->flags & GROUP_IS_CACHABLE)) {
			group->flags |= GROUP_IS_CACHED;
			group->flags &= ~GROUP_IS_CACHABLE;
			GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache on - size %d\n", gf_node_get_log_name(node), group->cached_size ));
			cache_on = 1;
		}
		/*group cache has been turned off in the previous frame*/
		else if (group->cache) {
			group_cache_del(group->cache);
			group->cache = NULL;
			group->changed = is_dirty;
			group->nb_stats_frame = 0;
			group->traverse_time = 0;
			GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache off\n", gf_node_get_log_name(node) ));
			return 0;
		}

		if (!cache_on) {
			if (is_dirty) {
				group->changed = 1;
			}
			/*ask for stats again*/
			else if (group->changed) {
				group->changed = 0;
				group->nb_stats_frame = 0;
				group->traverse_time = 0;
			} else if (zoom_changed) {
				group->nb_stats_frame = 0;
				group->traverse_time = 0;
			}
			if (is_dirty || (group->nb_stats_frame < NUM_STATS_FRAMES)) {
				/*force direct draw mode*/
				if (!is_dirty)
					tr_state->visual->compositor->traverse_state->invalidate_all = 1;
				/*force redraw*/
				tr_state->visual->compositor->draw_next_frame = 1;
			}
			return 0;
		}
	}
	/*cache is dirty*/
	else if (is_dirty) {
		/*permanent cache, just recompute*/
		if (group->flags & GROUP_PERMANENT_CACHE) {
			group->changed = 1;
			group->cache->force_recompute = 1;
		}
		/*otherwise destroy the cache*/
		else if (group->cache) {
			gf_cache_remove_entry(tr_state->visual->compositor, node, group);
			group_cache_del(group->cache);
			group->cache = NULL;
			group->flags &= ~GROUP_IS_CACHED;
			group->changed = 0;
			group->nb_stats_frame = 0;
			group->traverse_time = 0;
			GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache off due to sub-tree modifications\n", gf_node_get_log_name(node) ));
			return 0;
		}
	}
	/*zoom has changed*/
	else if (zoom_changed) {
		/*permanent cache, just recompute*/
		if (group->flags & GROUP_PERMANENT_CACHE) {
			group->changed = 1;
			group->cache->force_recompute = 1;
		}
		/*otherwise check if we accept this scale ratio or if we must recompute*/
		else if (group->cache) {
			Fixed scale = MAX(tr_state->transform.m[0], tr_state->transform.m[4]);

			if (100*scale >= group->cache->scale*(100 + tr_state->visual->compositor->cache_tolerance))
				zoom_changed = 1;
			else if ((100+tr_state->visual->compositor->cache_tolerance)*scale <= 100*group->cache->scale)
				zoom_changed = 1;
			else
				zoom_changed = 0;

			if (zoom_changed) {
				gf_cache_remove_entry(tr_state->visual->compositor, node, group);
				group_cache_del(group->cache);
				group->cache = NULL;
				group->flags &= ~GROUP_IS_CACHED;
				group->changed = 0;
				group->nb_stats_frame = 0;
				group->traverse_time = 0;
				GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning group %s cache off due to zoom changes\n", gf_node_get_log_name(node) ));
				return 0;
			}
		}
	}

	/*keep track of this cache object for later removal*/
	if (!(group->flags & GROUP_PERMANENT_CACHE))
		gf_list_add(tr_state->visual->compositor->cached_groups_queue, group);

	if (!group->cache) {
		/*ALLOCATE THE CACHE*/
		group->cache = group_cache_new(tr_state->visual->compositor, node);
		needs_recompute = 1;
	}

	/*cache has been modified due to node changes, reset stats*/
	group_cache_traverse(node, group->cache, tr_state, needs_recompute, 1, 0);
	return 1;
}
Beispiel #14
0
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);
}
static void visual_2d_fill_path(GF_VisualManager *visual, DrawableContext *ctx, GF_STENCIL stencil, GF_TraverseState *tr_state, Bool is_erase)
{
	Bool has_modif = GF_FALSE;
	GF_IRect clip;
	GF_Raster2D *raster = visual->compositor->rasterizer;

	/*background & direct drawing : use ctx clip*/
	if ((ctx->flags & CTX_IS_BACKGROUND) || tr_state->immediate_draw) {
		if (ctx->bi->clip.width && ctx->bi->clip.height) {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s[%s] (direct draw)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node) ));

			if (stencil) {
				raster->surface_set_clipper(visual->raster_surface, &ctx->bi->clip);
				raster->surface_fill(visual->raster_surface, stencil);
			} else {
				raster->surface_clear(visual->raster_surface, &ctx->bi->clip, 0);
			}

			has_modif = GF_TRUE;
		}
	}
	/*indirect drawing, draw path in all dirty areas*/
	else {
		u32 i;
		for (i=0; i<visual->to_redraw.count; i++) {
			/*there's an opaque region above, don't draw*/
#ifdef TRACK_OPAQUE_REGIONS
			if (!is_erase && (visual->draw_node_index<visual->to_redraw.list[i].opaque_node_index)) continue;
#endif
			clip = ctx->bi->clip;
			gf_irect_intersect(&clip, &visual->to_redraw.list[i].rect);
			if (clip.width && clip.height) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s[%s] (indirect draw @ dirty rect idx %d)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node), i));
				if (stencil) {
					raster->surface_set_clipper(visual->raster_surface, &clip);
					raster->surface_fill(visual->raster_surface, stencil);
				} else {
					raster->surface_clear(visual->raster_surface, &clip, 0);
				}
				has_modif = 1;
			}
		}
	}
#ifndef GPAC_DISABLE_3D
	if (!is_erase)
		visual->nb_objects_on_canvas_since_last_ogl_flush++;
#endif
	if (has_modif) {
		visual->has_modif = 1;
#ifndef GPAC_DISABLE_3D
		if (!visual->offscreen && visual->compositor->hybrid_opengl && !is_erase)
			ra_union_rect(&visual->hybgl_drawn, &ctx->bi->clip);
#endif
	}
}
Beispiel #16
0
static void visual_2d_fill_path(GF_VisualManager *visual, DrawableContext *ctx, GF_STENCIL stencil, GF_TraverseState *tr_state)
{
	GF_IRect clip;
	GF_Raster2D *raster = visual->compositor->rasterizer;

	/*background & direct drawing : use ctx clip*/
	if ((ctx->flags & CTX_IS_BACKGROUND) || tr_state->immediate_draw) {
		if (ctx->bi->clip.width && ctx->bi->clip.height) {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s[%s] (direct draw)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node) ));

			if (stencil) {
				raster->surface_set_clipper(visual->raster_surface, &ctx->bi->clip);
				raster->surface_fill(visual->raster_surface, stencil);
			} else {
				raster->surface_clear(visual->raster_surface, &ctx->bi->clip, 0);
			}

			visual->has_modif = 1;
		}
	}
	/*indirect drawing, draw path in all dirty areas*/
	else {
		u32 i;
		for (i=0; i<visual->to_redraw.count; i++) {
			/*there's an opaque region above, don't draw*/
#ifdef TRACK_OPAQUE_REGIONS
			if (visual->draw_node_index<visual->to_redraw.opaque_node_index[i]) continue;
#endif
			clip = ctx->bi->clip;
			gf_irect_intersect(&clip, &visual->to_redraw.list[i]);
			if (clip.width && clip.height) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s[%s] (indirect draw @ dirty rect idx %d)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node), i));
				if (stencil) {
					raster->surface_set_clipper(visual->raster_surface, &clip);
					raster->surface_fill(visual->raster_surface, stencil);
				} else {
					raster->surface_clear(visual->raster_surface, &clip, 0);
				}
				visual->has_modif = 1;
			}
		}
	}
}