The optimization of skipping the rendering of the layer's context only works for...
[synfig.git] / synfig-core / trunk / src / synfig / context.cpp
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 **
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.
14 **
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.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "context.h"
33 #include "layer.h"
34 #include "layer_composite.h"
35 #include "string.h"
36 #include "vector.h"
37 #include "color.h"
38 #include "surface.h"
39 #include "renddesc.h"
40 #include "valuenode.h"
41
42 #endif
43
44 /* === U S I N G =========================================================== */
45
46 using namespace std;
47 using namespace etl;
48 using namespace synfig;
49
50 /* === M A C R O S ========================================================= */
51
52 // #define SYNFIG_PROFILE_LAYERS
53 // #define SYNFIG_DEBUG_LAYERS
54
55 /* === G L O B A L S ======================================================= */
56
57 #ifdef SYNFIG_PROFILE_LAYERS
58 #include <ETL/clock>
59 static int depth(0);
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;
64 static void
65 _print_profile_report()
66 {
67         synfig::info(">>>> Profile Report: (Times are in msecs)");
68         std::map<String,float>::iterator iter;
69         float total_time(0);
70         for(iter=time_table.begin();iter!=time_table.end();++iter)
71         {
72                 String layer(iter->first);
73                 float time(iter->second);
74                 int runs(run_table[layer]);
75                 total_time+=time;
76                 synfig::info(" Layer \"%s\",\tExecs: %03d, Avg Time: %05.1f, Total Time: %05.1f",layer.c_str(),runs,time/runs*1000,time*1000);
77         }
78         synfig::info("Total Time: %f seconds", total_time);
79         synfig::info("<<<< End of Profile Report");
80 }
81 #endif  // SYNFIG_PROFILE_LAYERS
82
83 /* === P R O C E D U R E S ================================================= */
84
85 /* === M E T H O D S ======================================================= */
86
87 Color
88 Context::get_color(const Point &pos)const
89 {
90         Context context(*this);
91
92         while(!context->empty())
93         {
94                 // If this layer is active, then go
95                 // ahead and break out of the loop
96                 if((*context)->active())
97                         break;
98
99                 // Otherwise, we want to keep searching
100                 // till we find either an active layer,
101                 // or the end of the layer list
102                 ++context;
103         }
104
105         // If this layer isn't defined, return alpha
106         if((context)->empty()) return Color::alpha();
107
108         RWLock::ReaderLock lock((*context)->get_rw_lock());
109
110         return (*context)->get_color(context+1, pos);
111 }
112
113 Rect
114 Context::get_full_bounding_rect()const
115 {
116         Context context(*this);
117
118         while(!context->empty())
119         {
120                 // If this layer is active, then go
121                 // ahead and break out of the loop
122                 if((*context)->active())
123                         break;
124
125                 // Otherwise, we want to keep searching
126                 // till we find either an active layer,
127                 // or the end of the layer list
128                 ++context;
129         }
130
131         // If this layer isn't defined, return zero-sized rectangle
132         if(context->empty()) return Rect::zero();
133
134         return (*context)->get_full_bounding_rect(context+1);
135 }
136
137
138 /* Profiling will go like this:
139         Profile start = +, stop = -
140
141         +
142         -
143
144         time diff is recorded
145
146         to get the independent times we need to break at the one inside and record etc...
147         so it looks more like this:
148
149         +
150           -
151           +
152                 -
153                 +
154                         ...
155                 -
156                 +
157           -
158           +
159         -
160
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...
163 */
164
165 bool
166 Context::accelerated_render(Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb) const
167 {
168 #ifdef SYNFIG_PROFILE_LAYERS
169         String layer_name(curr_layer);
170
171         //sum the pre-work done by layer above us... (curr_layer is layer above us...)
172         if(depth>0)
173         {
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;
177         }
178 #endif  // SYNFIG_PROFILE_LAYERS
179
180         const Rect bbox(renddesc.get_rect());
181
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);
188
189         for(;!(context)->empty();++context)
190         {
191                 // If we are not active then move on to next layer
192                 if(!(*context)->active())
193                         continue;
194
195                 const Rect layer_bounds((*context)->get_bounding_rect());
196                 composite = etl::handle<Layer_Composite>::cast_dynamic(*context);
197
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))
203                 {
204                         if (composite &&
205                                 Color::is_straight(composite->get_blend_method()) &&
206                                 composite->get_amount() != 0.0f)
207                         {
208                                 straight_and_empty = true;
209                                 break;
210                         }
211                         continue;
212                 }
213
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
217                 if (composite &&
218                         composite->get_blend_method() == Color::BLEND_STRAIGHT &&
219                         composite->get_amount() == 1.0f &&
220                         !composite->reads_context())
221                 {
222                         Layer::Handle layer = *context;
223                         while (!context->empty()) context++; // skip the context
224                         return layer->accelerated_render(context,surface,quality,renddesc, cb);
225                 }
226
227                 // Break out of the loop--we have found a good layer
228                 break;
229         }
230
231         // If this layer isn't defined, return alpha
232         if (context->empty() || (straight_and_empty && composite->get_amount() == 1.0f))
233         {
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());
238                 surface->clear();
239 #ifdef SYNFIG_PROFILE_LAYERS
240                 profile_timer.reset();
241 #endif  // SYNFIG_PROFILE_LAYERS
242                 return true;
243         }
244
245 #ifdef SYNFIG_DEBUG_LAYERS
246         synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
247 #endif  // SYNFIG_DEBUG_LAYERS
248
249         try {
250                 RWLock::ReaderLock lock((*context)->get_rw_lock());
251
252 #ifdef SYNFIG_PROFILE_LAYERS
253         //go down one layer :P
254         depth++;
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
258
259         bool ret;
260
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)
266         {
267                 if (ret = Context((context+1)).accelerated_render(surface,quality,renddesc,cb))
268                 {
269                         Surface clearsurface;
270                         clearsurface.set_wh(renddesc.get_w(),renddesc.get_h());
271                         clearsurface.clear();
272
273                         Surface::alpha_pen apen(surface->begin());
274                         apen.set_alpha(composite->get_amount());
275                         apen.set_blend_method(composite->get_blend_method());
276
277                         clearsurface.blit_to(apen);
278                 }
279         }
280         else
281                 ret = (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
282
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;
288
289         depth--;
290         curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
291
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
296
297         return ret;
298         }
299         catch(std::bad_alloc)
300         {
301                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
302 #ifdef _DEBUG
303                 return false;
304 #else  // _DEBUG
305                 ++context;
306                 return context.accelerated_render(surface, quality, renddesc, cb);
307 #endif  // _DEBUG
308         }
309         catch(...)
310         {
311                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
312                 throw;
313         }
314 }
315
316 void
317 Context::set_time(Time time)const
318 {
319         Context context(*this);
320         while(!(context)->empty())
321         {
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))
325                         break;
326
327                 // Otherwise, we want to keep searching
328                 // till we find either an active layer,
329                 // or the end of the layer list
330                 ++context;
331         }
332
333         // If this layer isn't defined, just return
334         if((context)->empty()) return;
335
336         // Set up a writer lock
337         RWLock::WriterLock lock((*context)->get_rw_lock());
338
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);
341
342         {
343                 Layer::ParamList params;
344                 Layer::DynamicParamList::const_iterator iter;
345
346                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
347                         params[iter->first]=(*iter->second)(time);
348
349                 (*context)->set_param_list(params);
350
351                 (*context)->set_time(context+1,time);
352                 (*context)->dirty_time_=time;
353
354         }
355 }
356
357 void
358 Context::set_time(Time time,const Vector &/*pos*/)const
359 {
360         set_time(time);
361 /*
362         Context context(*this);
363         while(!(context)->empty())
364         {
365                 // If this layer is active, then go
366                 // ahead and break out of the loop
367                 if((*context)->active())
368                         break;
369
370                 // Otherwise, we want to keep searching
371                 // till we find either an active layer,
372                 // or the end of the layer list
373                 ++context;
374         }
375
376         // If this layer isn't defined, just return
377         if((context)->empty()) return;
378
379         else
380         {
381                 Layer::ParamList params;
382                 Layer::DynamicParamList::const_iterator iter;
383
384                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
385                         params[iter->first]=(*iter->second)(time);
386
387                 (*context)->set_param_list(params);
388
389                 (*context)->set_time(context+1,time,pos);
390         }
391 */
392 }
393
394 etl::handle<Layer>
395 Context::hit_check(const Point &pos)const
396 {
397         Context context(*this);
398
399         while(!context->empty())
400         {
401                 // If this layer is active, then go
402                 // ahead and break out of the loop
403                 if((*context)->active())
404                         break;
405
406                 // Otherwise, we want to keep searching
407                 // till we find either an active layer,
408                 // or the end of the layer list
409                 ++context;
410         }
411
412         // If this layer isn't defined, return an empty handle
413         if((context)->empty()) return 0;
414
415         return (*context)->hit_check(context+1, pos);
416 }