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