more updates
[synfig.git] / synfig-core / trunk / src / synfig / context.cpp
1 /* === S I N F G =========================================================== */
2 /*!     \file template.cpp
3 **      \brief Template File
4 **
5 **      $Id: context.cpp,v 1.4 2005/01/24 05:00:18 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **
10 **      This software and associated documentation
11 **      are CONFIDENTIAL and PROPRIETARY property of
12 **      the above-mentioned copyright holder.
13 **
14 **      You may not copy, print, publish, or in any
15 **      other way distribute this software without
16 **      a prior written agreement with
17 **      the copyright holder.
18 **      \endlegal
19 */
20 /* ========================================================================= */
21
22 /* === H E A D E R S ======================================================= */
23
24 #ifdef USING_PCH
25 #       include "pch.h"
26 #else
27 #ifdef HAVE_CONFIG_H
28 #       include <config.h>
29 #endif
30
31 #include "context.h"
32 #include "layer.h"
33 #include "string.h"
34 #include "vector.h"
35 #include "color.h"
36 #include "surface.h"
37 #include "renddesc.h"
38 #include "valuenode.h"
39
40 #endif
41
42 /* === U S I N G =========================================================== */
43
44 using namespace std;
45 using namespace etl;
46 using namespace sinfg;
47
48 /* === M A C R O S ========================================================= */
49
50 //#define SINFG_PROFILE_LAYERS
51 //#define SINFG_DEBUG_LAYERS
52
53 /* === G L O B A L S ======================================================= */
54
55 #ifdef SINFG_PROFILE_LAYERS
56 #include <ETL/clock>
57 static int depth(0);
58 static std::map<String,float> time_table;
59 static std::map<String,int> run_table;
60 static etl::clock profile_timer;
61 static String curr_layer;
62 static void
63 _print_profile_report()
64 {
65         sinfg::info(">>>> Profile Report: (Times are in msecs)");
66         std::map<String,float>::iterator iter;
67         float total_time(0);
68         for(iter=time_table.begin();iter!=time_table.end();++iter)
69         {
70                 String layer(iter->first);
71                 float time(iter->second);
72                 int runs(run_table[layer]);
73                 total_time+=time;
74                 sinfg::info(" Layer \"%s\",\tExecs: %03d, Avg Time: %05.1f, Total Time: %05.1f",layer.c_str(),runs,time/runs*1000,time*1000);
75         }
76         sinfg::info("Total Time: %f seconds", total_time);
77         sinfg::info("<<<< End of Profile Report");
78 }
79
80 #endif
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(context->empty())
131                 return Rect::zero();
132
133         return (*context)->get_full_bounding_rect(*this+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 SINFG_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
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 SINFG_DEBUG_LAYERS
209                 sinfg::info("Context::accelerated_render(): Hit end of list");
210 #endif
211                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
212                 surface->clear();
213                 #ifdef SINFG_PROFILE_LAYERS
214                 profile_timer.reset();  
215                 #endif
216                 return true;
217         }
218
219 #ifdef SINFG_DEBUG_LAYERS
220         sinfg::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
221 #endif
222
223         try {
224                 RWLock::ReaderLock lock((*context)->get_rw_lock());
225                 
226         #ifdef SINFG_PROFILE_LAYERS
227         
228         //go down one layer :P
229         depth++;
230         curr_layer=(*context)->get_name();      //make sure the layer inside is referring to the correct layer outside
231         profile_timer.reset();                                                                          // +
232         bool ret((*context)->accelerated_render(context+1,surface,quality,renddesc, cb));
233         
234         //post work for the previous layer
235         time_table[curr_layer]+=profile_timer();                                                        //-
236         if(run_table.count(curr_layer))run_table[curr_layer]++;
237                 else run_table[curr_layer]=1;
238
239         depth--;
240         curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
241                 
242         //print out the table it we're done...
243         if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
244         profile_timer.reset();                                                                                          //+
245         return ret;
246         #else
247         return (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
248         #endif
249
250         }
251         catch(std::bad_alloc)
252         {
253                 sinfg::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
254 #ifdef _DEBUG
255                 return false;
256 #else
257                 ++context;
258                 return context.accelerated_render(surface, quality, renddesc, cb);
259 #endif
260         }
261         catch(...)
262         {
263                 sinfg::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
264                 throw;          
265         }
266 }
267
268 void
269 Context::set_time(Time time)const
270 {
271         Context context(*this);
272         while(!(context)->empty())
273         {       
274                 // If this layer is active, then go
275                 // ahead and break out of the loop
276                 if((*context)->active() && !(*context)->dirty_time_.is_equal(time))
277                         break;
278                 
279                 // Otherwise, we want to keep searching
280                 // till we find either an active layer,
281                 // or the end of the layer list
282                 ++context;
283         }
284
285         // If this layer isn't defined, just return
286         if((context)->empty()) return;
287
288         // Set up a wrter lock
289         RWLock::WriterLock lock((*context)->get_rw_lock());
290
291         //sinfg::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
292         //sinfg::info("%s: time=%f",(*context)->get_name().c_str(),(float)time);
293
294         {
295                 Layer::ParamList params;
296                 Layer::DynamicParamList::const_iterator iter;
297                 
298                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
299                         params[iter->first]=(*iter->second)(time);
300                 
301                 (*context)->set_param_list(params);
302
303                 (*context)->set_time(context+1,time);
304                 (*context)->dirty_time_=time;
305
306         }
307 }
308
309 void
310 Context::set_time(Time time,const Vector &pos)const
311 {
312         set_time(time);
313 /*
314         Context context(*this);
315         while(!(context)->empty())
316         {       
317                 // If this layer is active, then go
318                 // ahead and break out of the loop
319                 if((*context)->active())
320                         break;
321                 
322                 // Otherwise, we want to keep searching
323                 // till we find either an active layer,
324                 // or the end of the layer list
325                 ++context;
326         }
327
328         // If this layer isn't defined, just return
329         if((context)->empty()) return;
330
331         else
332         {
333                 Layer::ParamList params;
334                 Layer::DynamicParamList::const_iterator iter;
335                 
336                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
337                         params[iter->first]=(*iter->second)(time);
338                 
339                 (*context)->set_param_list(params);
340
341                 (*context)->set_time(context+1,time,pos);
342         }
343 */
344 }
345
346 etl::handle<Layer>
347 Context::hit_check(const Point &pos)const
348 {
349         Context context(*this);
350         
351         while(!context->empty())
352         {       
353                 // If this layer is active, then go
354                 // ahead and break out of the loop
355                 if((*context)->active())
356                         break;
357                 
358                 // Otherwise, we want to keep searching
359                 // till we find either an active layer,
360                 // or the end of the layer list
361                 ++context;
362         }
363
364         // If this layer isn't defined, return an empty handle
365         if((context)->empty()) return 0;
366
367         return (*context)->hit_check(context+1, pos);
368 }