Tidying.
[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 "string.h"
35 #include "vector.h"
36 #include "color.h"
37 #include "surface.h"
38 #include "renddesc.h"
39 #include "valuenode.h"
40
41 #endif
42
43 /* === U S I N G =========================================================== */
44
45 using namespace std;
46 using namespace etl;
47 using namespace synfig;
48
49 /* === M A C R O S ========================================================= */
50
51 // #define SYNFIG_PROFILE_LAYERS
52 // #define SYNFIG_DEBUG_LAYERS
53
54 /* === G L O B A L S ======================================================= */
55
56 #ifdef SYNFIG_PROFILE_LAYERS
57 #include <ETL/clock>
58 static int depth(0);
59 static std::map<String,float> time_table;
60 static std::map<String,int> run_table;
61 static etl::clock profile_timer;
62 static String curr_layer;
63 static void
64 _print_profile_report()
65 {
66         synfig::info(">>>> Profile Report: (Times are in msecs)");
67         std::map<String,float>::iterator iter;
68         float total_time(0);
69         for(iter=time_table.begin();iter!=time_table.end();++iter)
70         {
71                 String layer(iter->first);
72                 float time(iter->second);
73                 int runs(run_table[layer]);
74                 total_time+=time;
75                 synfig::info(" Layer \"%s\",\tExecs: %03d, Avg Time: %05.1f, Total Time: %05.1f",layer.c_str(),runs,time/runs*1000,time*1000);
76         }
77         synfig::info("Total Time: %f seconds", total_time);
78         synfig::info("<<<< End of Profile Report");
79 }
80 #endif  // SYNFIG_PROFILE_LAYERS
81
82 /* === P R O C E D U R E S ================================================= */
83
84 /* === M E T H O D S ======================================================= */
85
86 Color
87 Context::get_color(const Point &pos)const
88 {
89         Context context(*this);
90
91         while(!context->empty())
92         {
93                 // If this layer is active, then go
94                 // ahead and break out of the loop
95                 if((*context)->active())
96                         break;
97
98                 // Otherwise, we want to keep searching
99                 // till we find either an active layer,
100                 // or the end of the layer list
101                 ++context;
102         }
103
104         // If this layer isn't defined, return alpha
105         if((context)->empty()) return Color::alpha();
106
107         RWLock::ReaderLock lock((*context)->get_rw_lock());
108
109         return (*context)->get_color(context+1, pos);
110 }
111
112 Rect
113 Context::get_full_bounding_rect()const
114 {
115         Context context(*this);
116
117         while(!context->empty())
118         {
119                 // If this layer is active, then go
120                 // ahead and break out of the loop
121                 if((*context)->active())
122                         break;
123
124                 // Otherwise, we want to keep searching
125                 // till we find either an active layer,
126                 // or the end of the layer list
127                 ++context;
128         }
129
130         // If this layer isn't defined, return zero-sized rectangle
131         if(context->empty()) return Rect::zero();
132
133         return (*context)->get_full_bounding_rect(context+1);
134 }
135
136
137 /* Profiling will go like this:
138         Profile start = +, stop = -
139
140         +
141         -
142
143         time diff is recorded
144
145         to get the independent times we need to break at the one inside and record etc...
146         so it looks more like this:
147
148         +
149           -
150           +
151                 -
152                 +
153                         ...
154                 -
155                 +
156           -
157           +
158         -
159
160         at each minus we must record all the info for that which we are worried about...
161         each layer can do work before or after the other work is done... so both values must be recorded...
162 */
163
164 bool
165 Context::accelerated_render(Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb) const
166 {
167 #ifdef SYNFIG_PROFILE_LAYERS
168         String layer_name(curr_layer);
169
170         //sum the pre-work done by layer above us... (curr_layer is layer above us...)
171         if(depth>0)
172         {
173                 time_table[curr_layer]+=profile_timer();
174                 //if(run_table.count(curr_layer))run_table[curr_layer]++;
175                 //      else run_table[curr_layer]=1;
176         }
177 #endif  // SYNFIG_PROFILE_LAYERS
178
179         const Rect bbox(renddesc.get_rect());
180
181         Context context(*this);
182         for(;!(context)->empty();++context)
183         {
184                 // If we are not active
185                 // then move on to next layer
186                 if(!(*context)->active())
187                         continue;
188
189                 const Rect layer_bounds((*context)->get_bounding_rect());
190
191                 // If the box area is less than zero
192                 // then move on to next layer
193                 if(layer_bounds.area()<=0.0000000000001)
194                         continue;
195
196                 // If the boxes do not intersect
197                 // then move on to next layer
198                 if(!(layer_bounds && bbox))
199                         continue;
200
201                 // Break out of the loop--we have found a good layer
202                 break;
203         }
204
205         // If this layer isn't defined, return alpha
206         if((context)->empty())
207         {
208 #ifdef SYNFIG_DEBUG_LAYERS
209                 synfig::info("Context::accelerated_render(): Hit end of list");
210 #endif  // SYNFIG_DEBUG_LAYERS
211                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
212                 surface->clear();
213 #ifdef SYNFIG_PROFILE_LAYERS
214                 profile_timer.reset();
215 #endif  // SYNFIG_PROFILE_LAYERS
216                 return true;
217         }
218
219 #ifdef SYNFIG_DEBUG_LAYERS
220         synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
221 #endif  // SYNFIG_DEBUG_LAYERS
222
223         try {
224                 RWLock::ReaderLock lock((*context)->get_rw_lock());
225
226 #ifdef SYNFIG_PROFILE_LAYERS
227         //go down one layer :P
228         depth++;
229         curr_layer=(*context)->get_name();      //make sure the layer inside is referring to the correct layer outside
230         profile_timer.reset();                                                                          // +
231         bool ret((*context)->accelerated_render(context+1,surface,quality,renddesc, cb));
232
233         //post work for the previous layer
234         time_table[curr_layer]+=profile_timer();                                                        //-
235         if(run_table.count(curr_layer))run_table[curr_layer]++;
236                 else run_table[curr_layer]=1;
237
238         depth--;
239         curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
240
241         //print out the table it we're done...
242         if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
243         profile_timer.reset();                                                                                          //+
244         return ret;
245 #else  // SYNFIG_PROFILE_LAYERS
246         return (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
247 #endif  // SYNFIG_PROFILE_LAYERS
248
249         }
250         catch(std::bad_alloc)
251         {
252                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
253 #ifdef _DEBUG
254                 return false;
255 #else  // _DEBUG
256                 ++context;
257                 return context.accelerated_render(surface, quality, renddesc, cb);
258 #endif  // _DEBUG
259         }
260         catch(...)
261         {
262                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
263                 throw;
264         }
265 }
266
267 void
268 Context::set_time(Time time)const
269 {
270         Context context(*this);
271         while(!(context)->empty())
272         {
273                 // If this layer is active, then go
274                 // ahead and break out of the loop
275                 if((*context)->active() && !(*context)->dirty_time_.is_equal(time))
276                         break;
277
278                 // Otherwise, we want to keep searching
279                 // till we find either an active layer,
280                 // or the end of the layer list
281                 ++context;
282         }
283
284         // If this layer isn't defined, just return
285         if((context)->empty()) return;
286
287         // Set up a writer lock
288         RWLock::WriterLock lock((*context)->get_rw_lock());
289
290         //synfig::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
291         //synfig::info("%s: time=%f",(*context)->get_name().c_str(),(float)time);
292
293         {
294                 Layer::ParamList params;
295                 Layer::DynamicParamList::const_iterator iter;
296
297                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
298                         params[iter->first]=(*iter->second)(time);
299
300                 (*context)->set_param_list(params);
301
302                 (*context)->set_time(context+1,time);
303                 (*context)->dirty_time_=time;
304
305         }
306 }
307
308 void
309 Context::set_time(Time time,const Vector &/*pos*/)const
310 {
311         set_time(time);
312 /*
313         Context context(*this);
314         while(!(context)->empty())
315         {
316                 // If this layer is active, then go
317                 // ahead and break out of the loop
318                 if((*context)->active())
319                         break;
320
321                 // Otherwise, we want to keep searching
322                 // till we find either an active layer,
323                 // or the end of the layer list
324                 ++context;
325         }
326
327         // If this layer isn't defined, just return
328         if((context)->empty()) return;
329
330         else
331         {
332                 Layer::ParamList params;
333                 Layer::DynamicParamList::const_iterator iter;
334
335                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
336                         params[iter->first]=(*iter->second)(time);
337
338                 (*context)->set_param_list(params);
339
340                 (*context)->set_time(context+1,time,pos);
341         }
342 */
343 }
344
345 etl::handle<Layer>
346 Context::hit_check(const Point &pos)const
347 {
348         Context context(*this);
349
350         while(!context->empty())
351         {
352                 // If this layer is active, then go
353                 // ahead and break out of the loop
354                 if((*context)->active())
355                         break;
356
357                 // Otherwise, we want to keep searching
358                 // till we find either an active layer,
359                 // or the end of the layer list
360                 ++context;
361         }
362
363         // If this layer isn't defined, return an empty handle
364         if((context)->empty()) return 0;
365
366         return (*context)->hit_check(context+1, pos);
367 }