Use an enumeration type rather than unnamed integers to specify the different types...
[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
81 #endif
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
179
180         const Rect bbox(renddesc.get_rect());
181
182         Context context(*this);
183         for(;!(context)->empty();++context)
184         {
185                 // If we are not active
186                 // then move on to next layer
187                 if(!(*context)->active())
188                         continue;
189
190                 const Rect layer_bounds((*context)->get_bounding_rect());
191
192                 // If the box area is less than zero
193                 // then move on to next layer
194                 if(layer_bounds.area()<=0.0000000000001)
195                         continue;
196
197                 // If the boxes do not intersect
198                 // then move on to next layer
199                 if(!(layer_bounds && bbox))
200                         continue;
201
202                 // Break out of the loop--we have found a good layer
203                 break;
204         }
205
206         // If this layer isn't defined, return alpha
207         if((context)->empty())
208         {
209 #ifdef SYNFIG_DEBUG_LAYERS
210                 synfig::info("Context::accelerated_render(): Hit end of list");
211 #endif
212                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
213                 surface->clear();
214                 #ifdef SYNFIG_PROFILE_LAYERS
215                 profile_timer.reset();
216                 #endif
217                 return true;
218         }
219
220 #ifdef SYNFIG_DEBUG_LAYERS
221         synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
222 #endif
223
224         try {
225                 RWLock::ReaderLock lock((*context)->get_rw_lock());
226
227         #ifdef SYNFIG_PROFILE_LAYERS
228
229         //go down one layer :P
230         depth++;
231         curr_layer=(*context)->get_name();      //make sure the layer inside is referring to the correct layer outside
232         profile_timer.reset();                                                                          // +
233         bool ret((*context)->accelerated_render(context+1,surface,quality,renddesc, cb));
234
235         //post work for the previous layer
236         time_table[curr_layer]+=profile_timer();                                                        //-
237         if(run_table.count(curr_layer))run_table[curr_layer]++;
238                 else run_table[curr_layer]=1;
239
240         depth--;
241         curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
242
243         //print out the table it we're done...
244         if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
245         profile_timer.reset();                                                                                          //+
246         return ret;
247         #else
248         return (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
249         #endif
250
251         }
252         catch(std::bad_alloc)
253         {
254                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
255 #ifdef _DEBUG
256                 return false;
257 #else
258                 ++context;
259                 return context.accelerated_render(surface, quality, renddesc, cb);
260 #endif
261         }
262         catch(...)
263         {
264                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
265                 throw;
266         }
267 }
268
269 void
270 Context::set_time(Time time)const
271 {
272         Context context(*this);
273         while(!(context)->empty())
274         {
275                 // If this layer is active, then go
276                 // ahead and break out of the loop
277                 if((*context)->active() && !(*context)->dirty_time_.is_equal(time))
278                         break;
279
280                 // Otherwise, we want to keep searching
281                 // till we find either an active layer,
282                 // or the end of the layer list
283                 ++context;
284         }
285
286         // If this layer isn't defined, just return
287         if((context)->empty()) return;
288
289         // Set up a wrter lock
290         RWLock::WriterLock lock((*context)->get_rw_lock());
291
292         //synfig::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
293         //synfig::info("%s: time=%f",(*context)->get_name().c_str(),(float)time);
294
295         {
296                 Layer::ParamList params;
297                 Layer::DynamicParamList::const_iterator iter;
298
299                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
300                         params[iter->first]=(*iter->second)(time);
301
302                 (*context)->set_param_list(params);
303
304                 (*context)->set_time(context+1,time);
305                 (*context)->dirty_time_=time;
306
307         }
308 }
309
310 void
311 Context::set_time(Time time,const Vector &/*pos*/)const
312 {
313         set_time(time);
314 /*
315         Context context(*this);
316         while(!(context)->empty())
317         {
318                 // If this layer is active, then go
319                 // ahead and break out of the loop
320                 if((*context)->active())
321                         break;
322
323                 // Otherwise, we want to keep searching
324                 // till we find either an active layer,
325                 // or the end of the layer list
326                 ++context;
327         }
328
329         // If this layer isn't defined, just return
330         if((context)->empty()) return;
331
332         else
333         {
334                 Layer::ParamList params;
335                 Layer::DynamicParamList::const_iterator iter;
336
337                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
338                         params[iter->first]=(*iter->second)(time);
339
340                 (*context)->set_param_list(params);
341
342                 (*context)->set_time(context+1,time,pos);
343         }
344 */
345 }
346
347 etl::handle<Layer>
348 Context::hit_check(const Point &pos)const
349 {
350         Context context(*this);
351
352         while(!context->empty())
353         {
354                 // If this layer is active, then go
355                 // ahead and break out of the loop
356                 if((*context)->active())
357                         break;
358
359                 // Otherwise, we want to keep searching
360                 // till we find either an active layer,
361                 // or the end of the layer list
362                 ++context;
363         }
364
365         // If this layer isn't defined, return an empty handle
366         if((context)->empty()) return 0;
367
368         return (*context)->hit_check(context+1, pos);
369 }