12f464f9c5d541cf1ed25c9a842cdc58eb8002b7
[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, and
323                 // it either isn't already set to the given time or
324                 //           it's a time loop layer,
325                 // then break out of the loop and set its time
326                 if((*context)->active() &&
327                    (!(*context)->dirty_time_.is_equal(time) ||
328                         (*context)->get_name() == "timeloop"))
329                         break;
330
331                 // Otherwise, we want to keep searching
332                 // till we find either an active layer,
333                 // or the end of the layer list
334                 ++context;
335         }
336
337         // If this layer isn't defined, just return
338         if((context)->empty()) return;
339
340         // Set up a writer lock
341         RWLock::WriterLock lock((*context)->get_rw_lock());
342
343         //synfig::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
344         //synfig::info("%s: time=%f",(*context)->get_name().c_str(),(float)time);
345
346         {
347                 Layer::ParamList params;
348                 Layer::DynamicParamList::const_iterator iter;
349
350                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
351                         params[iter->first]=(*iter->second)(time);
352
353                 (*context)->set_param_list(params);
354
355                 (*context)->set_time(context+1,time);
356                 (*context)->dirty_time_=time;
357
358         }
359 }
360
361 void
362 Context::set_time(Time time,const Vector &/*pos*/)const
363 {
364         set_time(time);
365 /*
366         Context context(*this);
367         while(!(context)->empty())
368         {
369                 // If this layer is active, then go
370                 // ahead and break out of the loop
371                 if((*context)->active())
372                         break;
373
374                 // Otherwise, we want to keep searching
375                 // till we find either an active layer,
376                 // or the end of the layer list
377                 ++context;
378         }
379
380         // If this layer isn't defined, just return
381         if((context)->empty()) return;
382
383         else
384         {
385                 Layer::ParamList params;
386                 Layer::DynamicParamList::const_iterator iter;
387
388                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
389                         params[iter->first]=(*iter->second)(time);
390
391                 (*context)->set_param_list(params);
392
393                 (*context)->set_time(context+1,time,pos);
394         }
395 */
396 }
397
398 etl::handle<Layer>
399 Context::hit_check(const Point &pos)const
400 {
401         Context context(*this);
402
403         while(!context->empty())
404         {
405                 // If this layer is active, then go
406                 // ahead and break out of the loop
407                 if((*context)->active())
408                         break;
409
410                 // Otherwise, we want to keep searching
411                 // till we find either an active layer,
412                 // or the end of the layer list
413                 ++context;
414         }
415
416         // If this layer isn't defined, return an empty handle
417         if((context)->empty()) return 0;
418
419         return (*context)->hit_check(context+1, pos);
420 }