1 /* === S Y N F I G ========================================================= */
3 ** \brief Template File
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
10 ** This package is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU General Public License as
12 ** published by the Free Software Foundation; either version 2 of
13 ** the License, or (at your option) any later version.
15 ** This package is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ** General Public License for more details.
21 /* ========================================================================= */
23 /* === H E A D E R S ======================================================= */
34 #include "layer_composite.h"
40 #include "valuenode.h"
44 /* === U S I N G =========================================================== */
48 using namespace synfig;
50 /* === M A C R O S ========================================================= */
52 // #define SYNFIG_PROFILE_LAYERS
53 // #define SYNFIG_DEBUG_LAYERS
55 /* === G L O B A L S ======================================================= */
57 #ifdef SYNFIG_PROFILE_LAYERS
60 static std::map<String,float> time_table;
61 static std::map<String,int> run_table;
62 static etl::clock profile_timer;
63 static String curr_layer;
65 _print_profile_report()
67 synfig::info(">>>> Profile Report: (Times are in msecs)");
68 std::map<String,float>::iterator iter;
70 for(iter=time_table.begin();iter!=time_table.end();++iter)
72 String layer(iter->first);
73 float time(iter->second);
74 int runs(run_table[layer]);
76 synfig::info(" Layer \"%s\",\tExecs: %03d, Avg Time: %05.1f, Total Time: %05.1f",layer.c_str(),runs,time/runs*1000,time*1000);
78 synfig::info("Total Time: %f seconds", total_time);
79 synfig::info("<<<< End of Profile Report");
81 #endif // SYNFIG_PROFILE_LAYERS
83 /* === P R O C E D U R E S ================================================= */
85 /* === M E T H O D S ======================================================= */
88 Context::get_color(const Point &pos)const
90 Context context(*this);
92 while(!context->empty())
94 // If this layer is active, then go
95 // ahead and break out of the loop
96 if((*context)->active())
99 // Otherwise, we want to keep searching
100 // till we find either an active layer,
101 // or the end of the layer list
105 // If this layer isn't defined, return alpha
106 if((context)->empty()) return Color::alpha();
108 RWLock::ReaderLock lock((*context)->get_rw_lock());
110 return (*context)->get_color(context+1, pos);
114 Context::get_full_bounding_rect()const
116 Context context(*this);
118 while(!context->empty())
120 // If this layer is active, then go
121 // ahead and break out of the loop
122 if((*context)->active())
125 // Otherwise, we want to keep searching
126 // till we find either an active layer,
127 // or the end of the layer list
131 // If this layer isn't defined, return zero-sized rectangle
132 if(context->empty()) return Rect::zero();
134 return (*context)->get_full_bounding_rect(context+1);
138 /* Profiling will go like this:
139 Profile start = +, stop = -
144 time diff is recorded
146 to get the independent times we need to break at the one inside and record etc...
147 so it looks more like this:
161 at each minus we must record all the info for that which we are worried about...
162 each layer can do work before or after the other work is done... so both values must be recorded...
166 Context::accelerated_render(Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb) const
168 #ifdef SYNFIG_PROFILE_LAYERS
169 String layer_name(curr_layer);
171 //sum the pre-work done by layer above us... (curr_layer is layer above us...)
174 time_table[curr_layer]+=profile_timer();
175 //if(run_table.count(curr_layer))run_table[curr_layer]++;
176 // else run_table[curr_layer]=1;
178 #endif // SYNFIG_PROFILE_LAYERS
180 const Rect bbox(renddesc.get_rect());
182 // this is going to be set to true if this layer contributes
183 // nothing, but it's a straight blend with non-zero amount, and so
184 // it has an effect anyway
185 bool straight_and_empty = false;
186 etl::handle<Layer_Composite> composite;
187 Context context(*this);
189 for(;!(context)->empty();++context)
191 // If we are not active then move on to next layer
192 if(!(*context)->active())
195 const Rect layer_bounds((*context)->get_bounding_rect());
196 composite = etl::handle<Layer_Composite>::cast_dynamic(*context);
198 // If the box area is less than zero or the boxes do not
199 // intersect then move on to next layer, unless the layer is
200 // using a straight blend and has a non-zero amount, in which
201 // case it will still affect the result
202 if(layer_bounds.area() <= 0.0000000000001 || !(layer_bounds && bbox))
205 Color::is_straight(composite->get_blend_method()) &&
206 composite->get_amount() != 0.0f)
208 straight_and_empty = true;
214 // If this layer has Straight as the blend method and amount
215 // is 1.0, and the layer doesn't depend on its context, then
216 // we don't want to render the context
218 composite->get_blend_method() == Color::BLEND_STRAIGHT &&
219 composite->get_amount() == 1.0f &&
220 !composite->reads_context())
222 Layer::Handle layer = *context;
223 while (!context->empty()) context++; // skip the context
224 return layer->accelerated_render(context,surface,quality,renddesc, cb);
227 // Break out of the loop--we have found a good layer
231 // If this layer isn't defined, return alpha
232 if (context->empty() || (straight_and_empty && composite->get_amount() == 1.0f))
234 #ifdef SYNFIG_DEBUG_LAYERS
235 synfig::info("Context::accelerated_render(): Hit end of list");
236 #endif // SYNFIG_DEBUG_LAYERS
237 surface->set_wh(renddesc.get_w(),renddesc.get_h());
239 #ifdef SYNFIG_PROFILE_LAYERS
240 profile_timer.reset();
241 #endif // SYNFIG_PROFILE_LAYERS
245 #ifdef SYNFIG_DEBUG_LAYERS
246 synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
247 #endif // SYNFIG_DEBUG_LAYERS
250 RWLock::ReaderLock lock((*context)->get_rw_lock());
252 #ifdef SYNFIG_PROFILE_LAYERS
253 //go down one layer :P
255 curr_layer=(*context)->get_name(); //make sure the layer inside is referring to the correct layer outside
256 profile_timer.reset(); // +
257 #endif // SYNFIG_PROFILE_LAYERS
261 // this layer doesn't draw anything onto the canvas we're
262 // rendering, but it uses straight blending, so we need to render
263 // the stuff under us and then blit transparent pixels over it
264 // using the appropriate 'amount'
265 if (straight_and_empty)
267 if ((ret = Context((context+1)).accelerated_render(surface,quality,renddesc,cb)))
269 Surface clearsurface;
270 clearsurface.set_wh(renddesc.get_w(),renddesc.get_h());
271 clearsurface.clear();
273 Surface::alpha_pen apen(surface->begin());
274 apen.set_alpha(composite->get_amount());
275 apen.set_blend_method(composite->get_blend_method());
277 clearsurface.blit_to(apen);
281 ret = (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
283 #ifdef SYNFIG_PROFILE_LAYERS
284 //post work for the previous layer
285 time_table[curr_layer]+=profile_timer(); //-
286 if(run_table.count(curr_layer))run_table[curr_layer]++;
287 else run_table[curr_layer]=1;
290 curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
292 //print out the table it we're done...
293 if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
294 profile_timer.reset(); //+
295 #endif // SYNFIG_PROFILE_LAYERS
299 catch(std::bad_alloc)
301 synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
306 return context.accelerated_render(surface, quality, renddesc, cb);
311 synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
317 Context::set_time(Time time)const
319 Context context(*this);
320 while(!(context)->empty())
322 // If this layer is active, then go
323 // ahead and break out of the loop
324 if((*context)->active() && !(*context)->dirty_time_.is_equal(time))
327 // Otherwise, we want to keep searching
328 // till we find either an active layer,
329 // or the end of the layer list
333 // If this layer isn't defined, just return
334 if((context)->empty()) return;
336 // Set up a writer lock
337 RWLock::WriterLock lock((*context)->get_rw_lock());
339 //synfig::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
340 //synfig::info("%s: time=%f",(*context)->get_name().c_str(),(float)time);
343 Layer::ParamList params;
344 Layer::DynamicParamList::const_iterator iter;
346 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
347 params[iter->first]=(*iter->second)(time);
349 (*context)->set_param_list(params);
351 (*context)->set_time(context+1,time);
352 (*context)->dirty_time_=time;
358 Context::set_time(Time time,const Vector &/*pos*/)const
362 Context context(*this);
363 while(!(context)->empty())
365 // If this layer is active, then go
366 // ahead and break out of the loop
367 if((*context)->active())
370 // Otherwise, we want to keep searching
371 // till we find either an active layer,
372 // or the end of the layer list
376 // If this layer isn't defined, just return
377 if((context)->empty()) return;
381 Layer::ParamList params;
382 Layer::DynamicParamList::const_iterator iter;
384 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
385 params[iter->first]=(*iter->second)(time);
387 (*context)->set_param_list(params);
389 (*context)->set_time(context+1,time,pos);
395 Context::hit_check(const Point &pos)const
397 Context context(*this);
399 while(!context->empty())
401 // If this layer is active, then go
402 // ahead and break out of the loop
403 if((*context)->active())
406 // Otherwise, we want to keep searching
407 // till we find either an active layer,
408 // or the end of the layer list
412 // If this layer isn't defined, return an empty handle
413 if((context)->empty()) return 0;
415 return (*context)->hit_check(context+1, pos);