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 composite->get_blend_method() == Color::BLEND_STRAIGHT &&
206 composite->get_amount() != 0.0f)
208 straight_and_empty = true;
214 // If this layer has Straight as the blend method and amount is 1.0
215 // then we don't want to render the context
216 if (composite && composite->get_blend_method() == Color::BLEND_STRAIGHT &&
217 composite->get_amount() == 1.0f)
219 Layer::Handle layer = *context;
220 while (!context->empty()) context++; // skip the context
221 return layer->accelerated_render(context,surface,quality,renddesc, cb);
224 // Break out of the loop--we have found a good layer
228 // If this layer isn't defined, return alpha
229 if (context->empty() || (straight_and_empty && composite->get_amount() == 1.0f))
231 #ifdef SYNFIG_DEBUG_LAYERS
232 synfig::info("Context::accelerated_render(): Hit end of list");
233 #endif // SYNFIG_DEBUG_LAYERS
234 surface->set_wh(renddesc.get_w(),renddesc.get_h());
236 #ifdef SYNFIG_PROFILE_LAYERS
237 profile_timer.reset();
238 #endif // SYNFIG_PROFILE_LAYERS
242 #ifdef SYNFIG_DEBUG_LAYERS
243 synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
244 #endif // SYNFIG_DEBUG_LAYERS
247 RWLock::ReaderLock lock((*context)->get_rw_lock());
249 #ifdef SYNFIG_PROFILE_LAYERS
250 //go down one layer :P
252 curr_layer=(*context)->get_name(); //make sure the layer inside is referring to the correct layer outside
253 profile_timer.reset(); // +
254 #endif // SYNFIG_PROFILE_LAYERS
258 // this layer doesn't draw anything onto the canvas we're
259 // rendering, but it uses straight blending, so we need to render
260 // the stuff under us and then blit transparent pixels over it
261 // using the appropriate 'amount'
262 if (straight_and_empty)
264 if (ret = Context((context+1)).accelerated_render(surface,quality,renddesc,cb))
266 Surface clearsurface;
267 clearsurface.set_wh(renddesc.get_w(),renddesc.get_h());
268 clearsurface.clear();
270 Surface::alpha_pen apen(surface->begin());
271 apen.set_alpha(composite->get_amount());
272 apen.set_blend_method(Color::BLEND_STRAIGHT);
274 clearsurface.blit_to(apen);
278 ret = (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
280 #ifdef SYNFIG_PROFILE_LAYERS
281 //post work for the previous layer
282 time_table[curr_layer]+=profile_timer(); //-
283 if(run_table.count(curr_layer))run_table[curr_layer]++;
284 else run_table[curr_layer]=1;
287 curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
289 //print out the table it we're done...
290 if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
291 profile_timer.reset(); //+
292 #endif // SYNFIG_PROFILE_LAYERS
296 catch(std::bad_alloc)
298 synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
303 return context.accelerated_render(surface, quality, renddesc, cb);
308 synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
314 Context::set_time(Time time)const
316 Context context(*this);
317 while(!(context)->empty())
319 // If this layer is active, then go
320 // ahead and break out of the loop
321 if((*context)->active() && !(*context)->dirty_time_.is_equal(time))
324 // Otherwise, we want to keep searching
325 // till we find either an active layer,
326 // or the end of the layer list
330 // If this layer isn't defined, just return
331 if((context)->empty()) return;
333 // Set up a writer lock
334 RWLock::WriterLock lock((*context)->get_rw_lock());
336 //synfig::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
337 //synfig::info("%s: time=%f",(*context)->get_name().c_str(),(float)time);
340 Layer::ParamList params;
341 Layer::DynamicParamList::const_iterator iter;
343 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
344 params[iter->first]=(*iter->second)(time);
346 (*context)->set_param_list(params);
348 (*context)->set_time(context+1,time);
349 (*context)->dirty_time_=time;
355 Context::set_time(Time time,const Vector &/*pos*/)const
359 Context context(*this);
360 while(!(context)->empty())
362 // If this layer is active, then go
363 // ahead and break out of the loop
364 if((*context)->active())
367 // Otherwise, we want to keep searching
368 // till we find either an active layer,
369 // or the end of the layer list
373 // If this layer isn't defined, just return
374 if((context)->empty()) return;
378 Layer::ParamList params;
379 Layer::DynamicParamList::const_iterator iter;
381 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
382 params[iter->first]=(*iter->second)(time);
384 (*context)->set_param_list(params);
386 (*context)->set_time(context+1,time,pos);
392 Context::hit_check(const Point &pos)const
394 Context context(*this);
396 while(!context->empty())
398 // If this layer is active, then go
399 // ahead and break out of the loop
400 if((*context)->active())
403 // Otherwise, we want to keep searching
404 // till we find either an active layer,
405 // or the end of the layer list
409 // If this layer isn't defined, return an empty handle
410 if((context)->empty()) return 0;
412 return (*context)->hit_check(context+1, pos);