1 /* === S Y N F I G ========================================================= */
3 ** \brief Template File
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2008 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
35 #include "layer_composite.h"
41 #include "valuenode.h"
45 /* === U S I N G =========================================================== */
49 using namespace synfig;
51 /* === M A C R O S ========================================================= */
53 // #define SYNFIG_PROFILE_LAYERS
54 // #define SYNFIG_DEBUG_LAYERS
56 /* === G L O B A L S ======================================================= */
58 #ifdef SYNFIG_PROFILE_LAYERS
61 static std::map<String,float> time_table;
62 static std::map<String,int> run_table;
63 static etl::clock profile_timer;
64 static String curr_layer;
66 _print_profile_report()
68 synfig::info(">>>> Profile Report: (Times are in msecs)");
69 std::map<String,float>::iterator iter;
71 for(iter=time_table.begin();iter!=time_table.end();++iter)
73 String layer(iter->first);
74 float time(iter->second);
75 int runs(run_table[layer]);
77 synfig::info(" Layer \"%s\",\tExecs: %03d, Avg Time: %05.1f, Total Time: %05.1f",layer.c_str(),runs,time/runs*1000,time*1000);
79 synfig::info("Total Time: %f seconds", total_time);
80 synfig::info("<<<< End of Profile Report");
82 #endif // SYNFIG_PROFILE_LAYERS
84 /* === P R O C E D U R E S ================================================= */
86 /* === M E T H O D S ======================================================= */
89 Context::get_color(const Point &pos)const
91 Context context(*this);
93 while(!context->empty())
95 // If this layer is active, then go
96 // ahead and break out of the loop
97 if((*context)->active())
100 // Otherwise, we want to keep searching
101 // till we find either an active layer,
102 // or the end of the layer list
106 // If this layer isn't defined, return alpha
107 if((context)->empty()) return Color::alpha();
109 RWLock::ReaderLock lock((*context)->get_rw_lock());
111 return (*context)->get_color(context+1, pos);
115 Context::get_full_bounding_rect()const
117 Context context(*this);
119 while(!context->empty())
121 // If this layer is active, then go
122 // ahead and break out of the loop
123 if((*context)->active())
126 // Otherwise, we want to keep searching
127 // till we find either an active layer,
128 // or the end of the layer list
132 // If this layer isn't defined, return zero-sized rectangle
133 if(context->empty()) return Rect::zero();
135 return (*context)->get_full_bounding_rect(context+1);
139 /* Profiling will go like this:
140 Profile start = +, stop = -
145 time diff is recorded
147 to get the independent times we need to break at the one inside and record etc...
148 so it looks more like this:
162 at each minus we must record all the info for that which we are worried about...
163 each layer can do work before or after the other work is done... so both values must be recorded...
167 Context::accelerated_render(Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb) const
169 #ifdef SYNFIG_PROFILE_LAYERS
170 String layer_name(curr_layer);
172 //sum the pre-work done by layer above us... (curr_layer is layer above us...)
175 time_table[curr_layer]+=profile_timer();
176 //if(run_table.count(curr_layer))run_table[curr_layer]++;
177 // else run_table[curr_layer]=1;
179 #endif // SYNFIG_PROFILE_LAYERS
181 const Rect bbox(renddesc.get_rect());
183 // this is going to be set to true if this layer contributes
184 // nothing, but it's a straight blend with non-zero amount, and so
185 // it has an effect anyway
186 bool straight_and_empty = false;
187 etl::handle<Layer_Composite> composite;
188 Context context(*this);
190 for(;!(context)->empty();++context)
192 // If we are not active then move on to next layer
193 if(!(*context)->active())
196 const Rect layer_bounds((*context)->get_bounding_rect());
197 composite = etl::handle<Layer_Composite>::cast_dynamic(*context);
199 // If the box area is less than zero or the boxes do not
200 // intersect then move on to next layer, unless the layer is
201 // using a straight blend and has a non-zero amount, in which
202 // case it will still affect the result
203 if(layer_bounds.area() <= 0.0000000000001 || !(layer_bounds && bbox))
206 Color::is_straight(composite->get_blend_method()) &&
207 composite->get_amount() != 0.0f)
209 straight_and_empty = true;
215 // If this layer has Straight as the blend method and amount
216 // is 1.0, and the layer doesn't depend on its context, then
217 // we don't want to render the context
219 composite->get_blend_method() == Color::BLEND_STRAIGHT &&
220 composite->get_amount() == 1.0f &&
221 !composite->reads_context())
223 Layer::Handle layer = *context;
224 while (!context->empty()) context++; // skip the context
225 return layer->accelerated_render(context,surface,quality,renddesc, cb);
228 // Break out of the loop--we have found a good layer
232 // If this layer isn't defined, return alpha
233 if (context->empty() || (straight_and_empty && composite->get_amount() == 1.0f))
235 #ifdef SYNFIG_DEBUG_LAYERS
236 synfig::info("Context::accelerated_render(): Hit end of list");
237 #endif // SYNFIG_DEBUG_LAYERS
238 surface->set_wh(renddesc.get_w(),renddesc.get_h());
240 #ifdef SYNFIG_PROFILE_LAYERS
241 profile_timer.reset();
242 #endif // SYNFIG_PROFILE_LAYERS
246 #ifdef SYNFIG_DEBUG_LAYERS
247 synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
248 #endif // SYNFIG_DEBUG_LAYERS
251 RWLock::ReaderLock lock((*context)->get_rw_lock());
253 #ifdef SYNFIG_PROFILE_LAYERS
254 //go down one layer :P
256 curr_layer=(*context)->get_name(); //make sure the layer inside is referring to the correct layer outside
257 profile_timer.reset(); // +
258 #endif // SYNFIG_PROFILE_LAYERS
262 // this layer doesn't draw anything onto the canvas we're
263 // rendering, but it uses straight blending, so we need to render
264 // the stuff under us and then blit transparent pixels over it
265 // using the appropriate 'amount'
266 if (straight_and_empty)
268 if ((ret = Context((context+1)).accelerated_render(surface,quality,renddesc,cb)))
270 Surface clearsurface;
271 clearsurface.set_wh(renddesc.get_w(),renddesc.get_h());
272 clearsurface.clear();
274 Surface::alpha_pen apen(surface->begin());
275 apen.set_alpha(composite->get_amount());
276 apen.set_blend_method(composite->get_blend_method());
278 clearsurface.blit_to(apen);
282 ret = (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
284 #ifdef SYNFIG_PROFILE_LAYERS
285 //post work for the previous layer
286 time_table[curr_layer]+=profile_timer(); //-
287 if(run_table.count(curr_layer))run_table[curr_layer]++;
288 else run_table[curr_layer]=1;
291 curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
293 //print out the table it we're done...
294 if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
295 profile_timer.reset(); //+
296 #endif // SYNFIG_PROFILE_LAYERS
300 catch(std::bad_alloc)
302 synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
307 return context.accelerated_render(surface, quality, renddesc, cb);
312 synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
318 Context::set_time(Time time)const
320 Context context(*this);
321 while(!(context)->empty())
323 // If this layer is active, and
324 // it either isn't already set to the given time or
325 // it's a time loop layer,
326 // then break out of the loop and set its time
327 if((*context)->active() &&
328 (!(*context)->dirty_time_.is_equal(time) ||
329 (*context)->get_name() == "timeloop"))
332 // Otherwise, we want to keep searching
333 // till we find either an active layer,
334 // or the end of the layer list
338 // If this layer isn't defined, just return
339 if((context)->empty()) return;
341 // Set up a writer lock
342 RWLock::WriterLock lock((*context)->get_rw_lock());
344 //synfig::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
345 //synfig::info("%s: time=%f",(*context)->get_name().c_str(),(float)time);
348 Layer::ParamList params;
349 Layer::DynamicParamList::const_iterator iter;
351 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
352 params[iter->first]=(*iter->second)(time);
354 (*context)->set_param_list(params);
356 (*context)->set_time(context+1,time);
357 (*context)->dirty_time_=time;
363 Context::set_time(Time time,const Vector &/*pos*/)const
367 Context context(*this);
368 while(!(context)->empty())
370 // If this layer is active, then go
371 // ahead and break out of the loop
372 if((*context)->active())
375 // Otherwise, we want to keep searching
376 // till we find either an active layer,
377 // or the end of the layer list
381 // If this layer isn't defined, just return
382 if((context)->empty()) return;
386 Layer::ParamList params;
387 Layer::DynamicParamList::const_iterator iter;
389 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
390 params[iter->first]=(*iter->second)(time);
392 (*context)->set_param_list(params);
394 (*context)->set_time(context+1,time,pos);
400 Context::hit_check(const Point &pos)const
402 Context context(*this);
404 while(!context->empty())
406 // If this layer is active, then go
407 // ahead and break out of the loop
408 if((*context)->active())
411 // Otherwise, we want to keep searching
412 // till we find either an active layer,
413 // or the end of the layer list
417 // If this layer isn't defined, return an empty handle
418 if((context)->empty()) return 0;
420 return (*context)->hit_check(context+1, pos);