1 /* === S Y N F I G ========================================================= */
2 /*!	\file context.cpp
3 **	\brief Template File
4 **
5 **	$Id$
6 **
7 **	\legal
8 **	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **	Copyright (c) 2008 Chris Moore
10 **	Copyright (c) 2012-2013 Carlos López
11 **
12 **	This package is free software; you can redistribute it and/or
13 **	modify it under the terms of the GNU General Public License as
14 **	published by the Free Software Foundation; either version 2 of
15 **	the License, or (at your option) any later version.
16 **
17 **	This package is distributed in the hope that it will be useful,
18 **	but WITHOUT ANY WARRANTY; without even the implied warranty of
19 **	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 **	General Public License for more details.
21 **	\endlegal
22 */
23 /* ========================================================================= */
24 
25 /* === H E A D E R S ======================================================= */
26 
27 #ifdef USING_PCH
28 #	include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #	include <config.h>
32 #endif
33 
34 #include "context.h"
35 
36 #include "general.h"
37 #include <synfig/localization.h>
38 #include "layer.h"
39 #include "string.h"
40 #include "vector.h"
41 #include "color.h"
42 #include "valuenode.h"
43 #include "transformation.h"
44 
45 #include "layers/layer_pastecanvas.h"
46 
47 #include "rendering/task.h"
48 #include "rendering/common/task/tasksurfaceempty.h"
49 
50 #endif
51 
52 /* === U S I N G =========================================================== */
53 
54 using namespace std;
55 using namespace etl;
56 using namespace synfig;
57 
58 /* === M A C R O S ========================================================= */
59 
60 // #define SYNFIG_PROFILE_LAYERS
61 // #define SYNFIG_DEBUG_LAYERS
62 
63 /* === G L O B A L S ======================================================= */
64 
65 #ifdef SYNFIG_PROFILE_LAYERS
66 #include <ETL/clock>
67 static int depth(0);
68 static std::map<String,float> time_table;
69 static std::map<String,int> run_table;
70 static etl::clock profile_timer;
71 static String curr_layer;
72 static void
_print_profile_report()73 _print_profile_report()
74 {
75 	synfig::info(">>>> Profile Report: (Times are in msecs)");
76 	std::map<String,float>::iterator iter;
77 	float total_time(0);
78 	for(iter=time_table.begin();iter!=time_table.end();++iter)
79 	{
80 		String layer(iter->first);
81 		float time(iter->second);
82 		int runs(run_table[layer]);
83 		total_time+=time;
84 		synfig::info(" Layer \"%s\",\tExecs: %03d, Avg Time: %05.1f, Total Time: %05.1f",layer.c_str(),runs,time/runs*1000,time*1000);
85 	}
86 	synfig::info("Total Time: %f seconds", total_time);
87 	synfig::info("<<<< End of Profile Report");
88 }
89 #endif	// SYNFIG_PROFILE_LAYERS
90 
91 /* === P R O C E D U R E S ================================================= */
92 
93 /* === M E T H O D S ======================================================= */
94 
95 
96 void
set_time(Time time,bool force) const97 IndependentContext::set_time(Time time, bool force)const
98 {
99 	IndependentContext context(*this);
100 	while(*context)
101 	{
102 		if ( (*context)->active() &&
103 		    (force || !(*context)->get_time_mark().is_equal(time)) )
104 			break;
105 		++context;
106 	}
107 	if (!*context) return;
108 
109 	// Set up a writer lock
110 	RWLock::WriterLock lock((*context)->get_rw_lock());
111 	(*context)->set_time(context+1,time);
112 }
113 
114 void
set_outline_grow(Real outline_grow) const115 IndependentContext::set_outline_grow(Real outline_grow)const
116 {
117 	IndependentContext context(*this);
118 	while(*context)
119 	{
120 		if ( (*context)->active()
121 		  && fabs((*context)->get_outline_grow_mark() - outline_grow) > 1e-8 )
122 			break;
123 		++context;
124 	}
125 	if (!*context) return;
126 
127 	// Set up a writer lock
128 	RWLock::WriterLock lock((*context)->get_rw_lock());
129 	(*context)->set_outline_grow(context+1, outline_grow);
130 }
131 
132 Color
get_color(const Point & pos) const133 Context::get_color(const Point &pos)const
134 {
135 	Context context(*this);
136 
137 	while(!context->empty())
138 	{
139 		// If this layer is active, then go
140 		// ahead and break out of the loop
141 		if(context.active() && context.in_z_range())
142 			break;
143 
144 		// Otherwise, we want to keep searching
145 		// till we find either an active layer,
146 		// or the end of the layer list
147 		++context;
148 	}
149 
150 	// If this layer isn't defined, return alpha
151 	if((context)->empty()) return Color::alpha();
152 
153 	RWLock::ReaderLock lock((*context)->get_rw_lock());
154 
155 	return (*context)->get_color(context.get_next(), pos);
156 }
157 
158 CairoColor
get_cairocolor(const Point & pos) const159 Context::get_cairocolor(const Point &pos)const
160 {
161 	Context context(*this);
162 
163 	while(!context->empty())
164 	{
165 		// If this layer is active, then go
166 		// ahead and break out of the loop
167 		if(context.active() && context.in_z_range())
168 			break;
169 
170 		// Otherwise, we want to keep searching
171 		// till we find either an active layer,
172 		// or the end of the layer list
173 		++context;
174 	}
175 
176 	// If this layer isn't defined, return alpha
177 	if((context)->empty()) return CairoColor::alpha();
178 
179 	RWLock::ReaderLock lock((*context)->get_rw_lock());
180 
181 	return (*context)->get_cairocolor(context.get_next(), pos);
182 }
183 
184 
185 Rect
get_full_bounding_rect() const186 Context::get_full_bounding_rect()const
187 {
188 	Context context(*this);
189 
190 	while(!context->empty())
191 	{
192 		// If this layer is active and visible in z_depth range,
193 		// then go ahead and break out of the loop
194 		if(context.active() && context.in_z_range())
195 			break;
196 
197 		// Otherwise, we want to keep searching
198 		// till we find either an active layer,
199 		// or the end of the layer list
200 		++context;
201 	}
202 
203 	// If this layer isn't defined, return zero-sized rectangle
204 	if(context->empty()) return Rect::zero();
205 
206 	return (*context)->get_full_bounding_rect(context.get_next());
207 }
208 
209 
210 /* Profiling will go like this:
211 	Profile start = +, stop = -
212 
213 	+
214 	-
215 
216 	time diff is recorded
217 
218 	to get the independent times we need to break at the one inside and record etc...
219 	so it looks more like this:
220 
221 	+
222 	  -
223 	  +
224 		-
225 		+
226 			...
227 		-
228 		+
229 	  -
230 	  +
231 	-
232 
233 	at each minus we must record all the info for that which we are worried about...
234 	each layer can do work before or after the other work is done... so both values must be recorded...
235 */
236 
237 
238 void
set_render_method(RenderMethod x)239 Context::set_render_method(RenderMethod x)
240 {
241 	Context context(*this);
242 
243 	if((context)->empty()) return;
244 
245 	(*context)->set_render_method(context.get_next(), x);
246 }
247 
248 etl::handle<Layer>
hit_check(const Point & pos) const249 Context::hit_check(const Point &pos)const
250 {
251 	Context context(*this);
252 
253 	while(!context->empty() && context.in_z_range())
254 	{
255 		// If this layer is active, then go
256 		// ahead and break out of the loop
257 		if(context.active())
258 			break;
259 
260 		// Otherwise, we want to keep searching
261 		// till we find either an active layer,
262 		// or the end of the layer list
263 		++context;
264 	}
265 
266 	// If this layer isn't defined, return an empty handle
267 	if((context)->empty()) return 0;
268 
269 	return (*context)->hit_check(context.get_next(), pos);
270 }
271 
272 
273 bool
accelerated_render(Surface * surface,int quality,const RendDesc & renddesc,ProgressCallback * cb) const274 Context::accelerated_render(Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb) const
275 {
276 #ifdef SYNFIG_PROFILE_LAYERS
277 	String layer_name(curr_layer);
278 	//sum the pre-work done by layer above us... (curr_layer is layer above us...)
279 	if(depth>0)
280 	{
281 		time_table[curr_layer]+=profile_timer();
282 		//if(run_table.count(curr_layer))run_table[curr_layer]++;
283 		//	else run_table[curr_layer]=1;
284 	}
285 #endif	// SYNFIG_PROFILE_LAYERS
286 
287 	const Rect bbox(renddesc.get_rect());
288 	const Matrix &transfromation_matrix(renddesc.get_transformation_matrix());
289 	// this is going to be set to true if this layer contributes
290 	// nothing, but it's a straight blend with non-zero amount, and so
291 	// it has an effect anyway
292 	bool straight_and_empty = false;
293 	etl::handle<Layer_Composite> composite;
294 	Context context(*this);
295 	// Run all layers until context is empty
296 	for(;!(context)->empty();++context)
297 	{
298 		// If we are not active then move on to next layer
299 		if(!context.active())
300 			continue;
301 		const Rect layer_bounds(Transformation::transform_bounds(transfromation_matrix, (*context)->get_bounding_rect()));
302 		// Cast current layer to composite
303 		composite = etl::handle<Layer_Composite>::cast_dynamic(*context);
304 		// If the box area is less than zero or the boxes do not
305 		// intersect then move on to next layer, unless the layer is
306 		// using a straight blend and has a non-zero amount, in which
307 		// case it will still affect the result
308 		if(layer_bounds.area() <= 0.0000000000001 || !(layer_bounds && bbox))
309 		{
310 			if (composite &&
311 				Surface::value_type::is_straight(composite->get_blend_method()) &&
312 				composite->get_amount() != 0.0f)
313 			{
314 				straight_and_empty = true;
315 				break;
316 			}
317 			continue;
318 		}
319 		// If this layer has Straight as the blend method and amount
320 		// is 1.0, and the layer doesn't depend on its context, then
321 		// we don't want to render the context
322 		if (composite &&
323 			composite->get_blend_method() == Color::BLEND_STRAIGHT &&
324 			composite->get_amount() == 1.0f &&
325 			!composite->reads_context())
326 		{
327 			Layer::Handle layer = *context;
328 			while (!context->empty()) context++; // skip the context
329 			return layer->accelerated_render(context,surface,quality,renddesc, cb);
330 		}
331 		// Break out of the loop--we have found a good layer
332 		break;
333 	}
334 	// If this layer isn't defined, return alpha
335 	if (context->empty() || (straight_and_empty && composite->get_amount() == 1.0f))
336 	{
337 #ifdef SYNFIG_DEBUG_LAYERS
338 		synfig::info("Context::accelerated_render(): Hit end of list");
339 #endif	// SYNFIG_DEBUG_LAYERS
340 		// resize the surface to the given render description
341 		surface->set_wh(renddesc.get_w(),renddesc.get_h());
342 		// and clear the surface
343 		surface->clear();
344 #ifdef SYNFIG_PROFILE_LAYERS
345 		profile_timer.reset();
346 #endif	// SYNFIG_PROFILE_LAYERS
347 		return true;
348 	}
349 
350 #ifdef SYNFIG_DEBUG_LAYERS
351 	synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
352 #endif	// SYNFIG_DEBUG_LAYERS
353 
354 	try {
355 		// lock the context for reading
356 		RWLock::ReaderLock lock((*context)->get_rw_lock());
357 #ifdef SYNFIG_PROFILE_LAYERS
358 		//go down one layer :P
359 		depth++;
360 		curr_layer=(*context)->get_name();	//make sure the layer inside is referring to the correct layer outside
361 		profile_timer.reset(); 										// +
362 #endif	// SYNFIG_PROFILE_LAYERS
363 		bool ret;
364 		// this layer doesn't draw anything onto the canvas we're
365 		// rendering, but it uses straight blending, so we need to render
366 		// the stuff under us and then blit transparent pixels over it
367 		// using the appropriate 'amount'
368 		if (straight_and_empty)
369 		{
370 			if ((ret = Context((context.get_next())).accelerated_render(surface,quality,renddesc,cb)))
371 			{
372 				Surface clearsurface;
373 				clearsurface.set_wh(renddesc.get_w(),renddesc.get_h());
374 				clearsurface.clear();
375 				Surface::alpha_pen apen(surface->begin());
376 				apen.set_alpha(composite->get_amount());
377 				apen.set_blend_method(composite->get_blend_method());
378 
379 				clearsurface.blit_to(apen);
380 			}
381 		}
382 		else
383 			ret = (*context)->accelerated_render(context.get_next(),surface,quality,renddesc, cb);
384 #ifdef SYNFIG_PROFILE_LAYERS
385 		//post work for the previous layer
386 		time_table[curr_layer]+=profile_timer();							//-
387 		if(run_table.count(curr_layer))run_table[curr_layer]++;
388 		else run_table[curr_layer]=1;
389 		depth--;
390 		curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
391 		//print out the table it we're done...
392 		if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
393 		profile_timer.reset();												//+
394 #endif	// SYNFIG_PROFILE_LAYERS
395 		return ret;
396 	}
397 	catch(std::bad_alloc)
398 	{
399 		synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
400 #ifdef _DEBUG
401 		return false;
402 #else  // _DEBUG
403 		++context;
404 		return context.accelerated_render(surface, quality, renddesc, cb);
405 #endif	// _DEBUG
406 	}
407 	catch(...)
408 	{
409 		synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
410 		throw;
411 	}
412 }
413 
414 
415 bool
accelerated_cairorender(cairo_t * cr,int quality,const RendDesc & renddesc,ProgressCallback * cb) const416 Context::accelerated_cairorender(cairo_t *cr,int quality, const RendDesc &renddesc, ProgressCallback *cb) const
417 {
418 #ifdef SYNFIG_PROFILE_LAYERS
419 	String layer_name(curr_layer);
420 	//sum the pre-work done by layer above us... (curr_layer is layer above us...)
421 	if(depth>0)
422 	{
423 		time_table[curr_layer]+=profile_timer();
424 	}
425 #endif	// SYNFIG_PROFILE_LAYERS
426 
427 	Context context(*this);
428 	// Run all layers until context is empty
429 	for(;!(context)->empty();++context)
430 	{
431 		// If we are not active then move on to next layer
432 		if(!context.active())
433 			continue;
434 		// Found one good layer
435 		break;
436 	}
437 	// If this layer isn't defined, return alpha
438 	if (context->empty())
439 	{
440 #ifdef SYNFIG_DEBUG_LAYERS
441 		synfig::info("Context::accelerated_cairorender(): Hit end of list");
442 #endif	// SYNFIG_DEBUG_LAYERS
443 		// clear the surface
444 		cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
445 		cairo_paint(cr);
446 #ifdef SYNFIG_PROFILE_LAYERS
447 		profile_timer.reset();
448 #endif	// SYNFIG_PROFILE_LAYERS
449 		return true;
450 	}
451 
452 #ifdef SYNFIG_DEBUG_LAYERS
453 	synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
454 #endif	// SYNFIG_DEBUG_LAYERS
455 
456 	try {
457 		// lock the context for reading
458 		RWLock::ReaderLock lock((*context)->get_rw_lock());
459 #ifdef SYNFIG_PROFILE_LAYERS
460 		//go down one layer :P
461 		depth++;
462 		curr_layer=(*context)->get_name();	//make sure the layer inside is referring to the correct layer outside
463 		profile_timer.reset(); 										// +
464 #endif	// SYNFIG_PROFILE_LAYERS
465 		bool ret;
466 		// this layer doesn't draw anything onto the canvas we're
467 		// rendering, but it uses straight blending, so we need to render
468 		// the stuff under us and then blit transparent pixels over it
469 		// using the appropriate 'amount'
470 		ret = (*context)->accelerated_cairorender(context.get_next(),cr,quality,renddesc, cb);
471 #ifdef SYNFIG_PROFILE_LAYERS
472 		//post work for the previous layer
473 		time_table[curr_layer]+=profile_timer();							//-
474 		if(run_table.count(curr_layer))run_table[curr_layer]++;
475 		else run_table[curr_layer]=1;
476 		depth--;
477 		curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
478 		//print out the table it we're done...
479 		if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
480 		profile_timer.reset();												//+
481 #endif	// SYNFIG_PROFILE_LAYERS
482 		return ret;
483 	}
484 	catch(std::bad_alloc)
485 	{
486 		synfig::error("Context::accelerated_cairorender(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
487 #ifdef _DEBUG
488 		return false;
489 #else  // _DEBUG
490 		++context;
491 		return context.accelerated_cairorender(cr, quality, renddesc, cb);
492 #endif	// _DEBUG
493 	}
494 	catch(...)
495 	{
496 		synfig::error("Context::accelerated_cairorender(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
497 		throw;
498 	}
499 }
500 
501 //!	Make rendering task using ContextParams
502 rendering::Task::Handle
build_rendering_task() const503 Context::build_rendering_task() const
504 {
505 	Context context = *this;
506 	while ( *context
507 		 && ( !context.active()
508 		   || ( !get_params().render_excluded_contexts
509 			 && (*context)->get_exclude_from_rendering() )))
510 		++context;
511 
512 	// TODO: apply z_range and z_blur (now applies in Canvas::optimize_layers)
513 
514 	return *context
515 		 ? (*context)->build_rendering_task(context.get_next())
516 		 : rendering::Task::Handle(new rendering::TaskSurfaceEmpty());
517 }
518 
519