void
OptimizerBlendZero::apply_zero(const RunParams &params, const TaskBlend::Handle &blend, const Task::Handle &task) const
{
	if (!task || !task->valid_target())
	{
		Task::Handle empty = new TaskSurfaceEmpty();
		empty->target_surface = params.ref_task->target_surface;
		apply(params, empty);
		return;
	}

	if (task->target_surface == blend->target_surface)
	{
		apply(params, task);
		return;
	}

	apply(params, task->clone());
	params.ref_task->target_surface = blend->target_surface;
	params.ref_task->move_target_rect(
		  blend->get_target_offset()
		- task->get_target_offset()
		+ (task == blend->sub_task_a() ? blend->offset_a : blend->offset_b) );
	assert( params.ref_task->check() );
}
void
OptimizerBlendSplit::run(const RunParams& params) const
{
	TaskBlend::Handle blend = TaskBlend::Handle::cast_dynamic(params.ref_task);
	if ( blend
	  && blend->target_surface
	  && blend->sub_task_a()
	  && blend->sub_task_a()->target_surface == blend->target_surface
	  && (blend->sub_task_a().type_is<TaskSurface>() || blend->sub_task_a().type_is<TaskSurfaceEmpty>())
	  && blend->sub_task_b()
	  && blend->sub_task_b()->target_surface
	  && !Color::is_straight(blend->blend_method) )
	{
		if (TaskList::Handle list_b = TaskList::Handle::cast_dynamic(blend->sub_task_b()))
		{
			// try to find dedicated groups
			std::vector<RectInt> groups;
			for(Task::List::const_iterator i = list_b->sub_tasks.begin(); i != list_b->sub_tasks.end(); ++i)
				if (*i && (*i)->valid_target())
					groups.push_back((*i)->get_target_rect());

			#ifndef	NDEBUG
			int sub_tasks_count = (int)groups.size();
			#endif

			bool retry = true;
			while(retry)
			{
				retry = false;
				for(int i = 0; i < (int)groups.size(); ++i)
				{
					for(int j = i+1; j < (int)groups.size(); ++j)
					{
						if (etl::intersect(groups[i], groups[j]))
						{
							etl::set_union(groups[i], groups[i], groups[j]);
							groups.erase(groups.begin() + j);
							--j;
							retry = true;
						}
					}
				}
			}

			// split task
			if (groups.size() > 1)
			{
				// create list
				TaskList::Handle list;
				list = new TaskList();
				assign(list, Task::Handle(blend));
				list->sub_tasks.clear();

				#ifndef	NDEBUG
				Task::Set processed;
				#endif

				// fill list
				for(int j = 0; j < (int)groups.size(); ++j)
				{
					// create sub-blend
					TaskList::Handle sub_list_b = TaskList::Handle::cast_dynamic(blend->sub_task_b()->clone());
					sub_list_b->sub_tasks.clear();
					sub_list_b->trunc_target_rect(groups[j]);

					RectInt rect = groups[j]
								 + blend->get_target_offset()
								 + blend->get_offset_b();
					TaskBlend::Handle sub_blend = TaskBlend::Handle::cast_dynamic(blend->clone());
					sub_blend->trunc_target_rect(rect);
					sub_blend->sub_task_a() = new TaskSurface();
					assign(sub_blend->sub_task_a(), blend->sub_task_a());
					sub_blend->sub_task_a()->sub_tasks.clear();
					sub_blend->sub_task_a()->trunc_target_rect(rect);
					sub_blend->sub_task_b() = sub_list_b;

					list->sub_tasks.push_back(sub_blend);

					// fill list-b of sub-blend
					for(Task::List::const_iterator i = list_b->sub_tasks.begin(); i != list_b->sub_tasks.end(); ++i)
						if (*i && (*i)->valid_target() && etl::contains(groups[j], (*i)->get_target_rect()))
						{
							#ifndef	NDEBUG
							assert(processed.count(*i) == 0);
							processed.insert(*i);
							#endif
							sub_list_b->sub_tasks.push_back(*i);
						}

					// optimization for list with single task
					if (sub_list_b->sub_tasks.size() == 1)
						sub_blend->sub_task_b() = sub_list_b->sub_tasks[0];
				}

				#ifndef	NDEBUG
				assert((int)processed.size() == sub_tasks_count);
				#endif

				apply(params, list);
			}
		}
	}
}
void
OptimizerBlendZero::run(const RunParams& params) const
{
	TaskBlend::Handle blend = TaskBlend::Handle::cast_dynamic(params.ref_task);
	if ( blend
	  && blend->target_surface
	  && blend->sub_task_a()
	  && blend->sub_task_a()->target_surface
	  && blend->sub_task_b()
	  && blend->sub_task_a()->target_surface)
	{
		bool zero_amount = fabsf(blend->amount) <= 1e-6;

		if (zero_amount)
			{ apply_zero(params, blend, blend->sub_task_a()); return; }

		bool valid_a = blend->sub_task_a()->valid_target();
		bool valid_b = blend->sub_task_b()->valid_target();

		if (!valid_b && !valid_a)
			{ apply_zero(params, blend, Task::Handle()); return; }

		bool one_amount = fabsf(blend->amount - 1.f) <= 1e-6;
		bool intertsects = valid_a && valid_b
						&& etl::intersect(blend->sub_task_a()->get_target_rect(), blend->sub_task_b()->get_target_rect());

		if (one_amount && !intertsects)
		{
			bool onto = Color::is_onto(blend->blend_method);
			bool straight = Color::is_straight(blend->blend_method);

			if ( onto && straight)
				{ apply_zero(params, blend, Task::Handle()); return; }
			if ( onto )
				{ apply_zero(params, blend, blend->sub_task_a()); return; }
			//if ( straight )
			//	{ apply_zero(params, blend, blend->sub_task_b()); return; }
		}
	}
}