Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / 0.61.09 / 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 **      Copyright (c) 2008 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "context.h"
34 #include "layer.h"
35 #include "layer_composite.h"
36 #include "string.h"
37 #include "vector.h"
38 #include "color.h"
39 #include "surface.h"
40 #include "renddesc.h"
41 #include "valuenode.h"
42
43 #endif
44
45 /* === U S I N G =========================================================== */
46
47 using namespace std;
48 using namespace etl;
49 using namespace synfig;
50
51 /* === M A C R O S ========================================================= */
52
53 // #define SYNFIG_PROFILE_LAYERS
54 // #define SYNFIG_DEBUG_LAYERS
55
56 /* === G L O B A L S ======================================================= */
57
58 #ifdef SYNFIG_PROFILE_LAYERS
59 #include <ETL/clock>
60 static int depth(0);
61 static std::map<String,float> time_table;
62 static std::map<String,int> run_table;
63 static etl::clock profile_timer;
64 static String curr_layer;
65 static void
66 _print_profile_report()
67 {
68         synfig::info(">>>> Profile Report: (Times are in msecs)");
69         std::map<String,float>::iterator iter;
70         float total_time(0);
71         for(iter=time_table.begin();iter!=time_table.end();++iter)
72         {
73                 String layer(iter->first);
74                 float time(iter->second);
75                 int runs(run_table[layer]);
76                 total_time+=time;
77                 synfig::info(" Layer \"%s\",\tExecs: %03d, Avg Time: %05.1f, Total Time: %05.1f",layer.c_str(),runs,time/runs*1000,time*1000);
78         }
79         synfig::info("Total Time: %f seconds", total_time);
80         synfig::info("<<<< End of Profile Report");
81 }
82 #endif  // SYNFIG_PROFILE_LAYERS
83
84 /* === P R O C E D U R E S ================================================= */
85
86 /* === M E T H O D S ======================================================= */
87
88 Color
89 Context::get_color(const Point &pos)const
90 {
91         Context context(*this);
92
93         while(!context->empty())
94         {
95                 // If this layer is active, then go
96                 // ahead and break out of the loop
97                 if((*context)->active())
98                         break;
99
100                 // Otherwise, we want to keep searching
101                 // till we find either an active layer,
102                 // or the end of the layer list
103                 ++context;
104         }
105
106         // If this layer isn't defined, return alpha
107         if((context)->empty()) return Color::alpha();
108
109         RWLock::ReaderLock lock((*context)->get_rw_lock());
110
111         return (*context)->get_color(context+1, pos);
112 }
113
114 Rect
115 Context::get_full_bounding_rect()const
116 {
117         Context context(*this);
118
119         while(!context->empty())
120         {
121                 // If this layer is active, then go
122                 // ahead and break out of the loop
123                 if((*context)->active())
124                         break;
125
126                 // Otherwise, we want to keep searching
127                 // till we find either an active layer,
128                 // or the end of the layer list
129                 ++context;
130         }
131
132         // If this layer isn't defined, return zero-sized rectangle
133         if(context->empty()) return Rect::zero();
134
135         return (*context)->get_full_bounding_rect(context+1);
136 }
137
138
139 /* Profiling will go like this:
140         Profile start = +, stop = -
141
142         +
143         -
144
145         time diff is recorded
146
147         to get the independent times we need to break at the one inside and record etc...
148         so it looks more like this:
149
150         +
151           -
152           +
153                 -
154                 +
155                         ...
156                 -
157                 +
158           -
159           +
160         -
161
162         at each minus we must record all the info for that which we are worried about...
163         each layer can do work before or after the other work is done... so both values must be recorded...
164 */
165
166 bool
167 Context::accelerated_render(Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb) const
168 {
169 #ifdef SYNFIG_PROFILE_LAYERS
170         String layer_name(curr_layer);
171
172         //sum the pre-work done by layer above us... (curr_layer is layer above us...)
173         if(depth>0)
174         {
175                 time_table[curr_layer]+=profile_timer();
176                 //if(run_table.count(curr_layer))run_table[curr_layer]++;
177                 //      else run_table[curr_layer]=1;
178         }
179 #endif  // SYNFIG_PROFILE_LAYERS
180
181         const Rect bbox(renddesc.get_rect());
182
183         // this is going to be set to true if this layer contributes
184         // nothing, but it's a straight blend with non-zero amount, and so
185         // it has an effect anyway
186         bool straight_and_empty = false;
187         etl::handle<Layer_Composite> composite;
188         Context context(*this);
189
190         for(;!(context)->empty();++context)
191         {
192                 // If we are not active then move on to next layer
193                 if(!(*context)->active())
194                         continue;
195
196                 const Rect layer_bounds((*context)->get_bounding_rect());
197                 composite = etl::handle<Layer_Composite>::cast_dynamic(*context);
198
199                 // If the box area is less than zero or the boxes do not
200                 // intersect then move on to next layer, unless the layer is
201                 // using a straight blend and has a non-zero amount, in which
202                 // case it will still affect the result
203                 if(layer_bounds.area() <= 0.0000000000001 || !(layer_bounds && bbox))
204                 {
205                         if (composite &&
206                                 Color::is_straight(composite->get_blend_method()) &&
207                                 composite->get_amount() != 0.0f)
208                         {
209                                 straight_and_empty = true;
210                                 break;
211                         }
212                         continue;
213                 }
214
215                 // If this layer has Straight as the blend method and amount
216                 // is 1.0, and the layer doesn't depend on its context, then
217                 // we don't want to render the context
218                 if (composite &&
219                         composite->get_blend_method() == Color::BLEND_STRAIGHT &&
220                         composite->get_amount() == 1.0f &&
221                         !composite->reads_context())
222                 {
223                         Layer::Handle layer = *context;
224                         while (!context->empty()) context++; // skip the context
225                         return layer->accelerated_render(context,surface,quality,renddesc, cb);
226                 }
227
228                 // Break out of the loop--we have found a good layer
229                 break;
230         }
231
232         // If this layer isn't defined, return alpha
233         if (context->empty() || (straight_and_empty && composite->get_amount() == 1.0f))
234         {
235 #ifdef SYNFIG_DEBUG_LAYERS
236                 synfig::info("Context::accelerated_render(): Hit end of list");
237 #endif  // SYNFIG_DEBUG_LAYERS
238                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
239                 surface->clear();
240 #ifdef SYNFIG_PROFILE_LAYERS
241                 profile_timer.reset();
242 #endif  // SYNFIG_PROFILE_LAYERS
243                 return true;
244         }
245
246 #ifdef SYNFIG_DEBUG_LAYERS
247         synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
248 #endif  // SYNFIG_DEBUG_LAYERS
249
250         try {
251                 RWLock::ReaderLock lock((*context)->get_rw_lock());
252
253 #ifdef SYNFIG_PROFILE_LAYERS
254         //go down one layer :P
255         depth++;
256         curr_layer=(*context)->get_name();      //make sure the layer inside is referring to the correct layer outside
257         profile_timer.reset();                                                                          // +
258 #endif  // SYNFIG_PROFILE_LAYERS
259
260         bool ret;
261
262         // this layer doesn't draw anything onto the canvas we're
263         // rendering, but it uses straight blending, so we need to render
264         // the stuff under us and then blit transparent pixels over it
265         // using the appropriate 'amount'
266         if (straight_and_empty)
267         {
268                 if ((ret = Context((context+1)).accelerated_render(surface,quality,renddesc,cb)))
269                 {
270                         Surface clearsurface;
271                         clearsurface.set_wh(renddesc.get_w(),renddesc.get_h());
272                         clearsurface.clear();
273
274                         Surface::alpha_pen apen(surface->begin());
275                         apen.set_alpha(composite->get_amount());
276                         apen.set_blend_method(composite->get_blend_method());
277
278                         clearsurface.blit_to(apen);
279                 }
280         }
281         else
282                 ret = (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
283
284 #ifdef SYNFIG_PROFILE_LAYERS
285         //post work for the previous layer
286         time_table[curr_layer]+=profile_timer();                                                        //-
287         if(run_table.count(curr_layer))run_table[curr_layer]++;
288                 else run_table[curr_layer]=1;
289
290         depth--;
291         curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
292
293         //print out the table it we're done...
294         if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
295         profile_timer.reset();                                                                                          //+
296 #endif  // SYNFIG_PROFILE_LAYERS
297
298         return ret;
299         }
300         catch(std::bad_alloc)
301         {
302                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
303 #ifdef _DEBUG
304                 return false;
305 #else  // _DEBUG
306                 ++context;
307                 return context.accelerated_render(surface, quality, renddesc, cb);
308 #endif  // _DEBUG
309         }
310         catch(...)
311         {
312                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
313                 throw;
314         }
315 }
316
317 void
318 Context::set_time(Time time)const
319 {
320         Context context(*this);
321         while(!(context)->empty())
322         {
323                 // If this layer is active, and
324                 // it either isn't already set to the given time or
325                 //           it's a time loop layer,
326                 // then break out of the loop and set its time
327                 if((*context)->active() &&
328                    (!(*context)->dirty_time_.is_equal(time) ||
329                         (*context)->get_name() == "timeloop"))
330                         break;
331
332                 // Otherwise, we want to keep searching
333                 // till we find either an active layer,
334                 // or the end of the layer list
335                 ++context;
336         }
337
338         // If this layer isn't defined, just return
339         if((context)->empty()) return;
340
341         // Set up a writer lock
342         RWLock::WriterLock lock((*context)->get_rw_lock());
343
344         //synfig::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
345         //synfig::info("%s: time=%f",(*context)->get_name().c_str(),(float)time);
346
347         {
348                 Layer::ParamList params;
349                 Layer::DynamicParamList::const_iterator iter;
350
351                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
352                         params[iter->first]=(*iter->second)(time);
353
354                 (*context)->set_param_list(params);
355
356                 (*context)->set_time(context+1,time);
357                 (*context)->dirty_time_=time;
358
359         }
360 }
361
362 void
363 Context::set_time(Time time,const Vector &/*pos*/)const
364 {
365         set_time(time);
366 /*
367         Context context(*this);
368         while(!(context)->empty())
369         {
370                 // If this layer is active, then go
371                 // ahead and break out of the loop
372                 if((*context)->active())
373                         break;
374
375                 // Otherwise, we want to keep searching
376                 // till we find either an active layer,
377                 // or the end of the layer list
378                 ++context;
379         }
380
381         // If this layer isn't defined, just return
382         if((context)->empty()) return;
383
384         else
385         {
386                 Layer::ParamList params;
387                 Layer::DynamicParamList::const_iterator iter;
388
389                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
390                         params[iter->first]=(*iter->second)(time);
391
392                 (*context)->set_param_list(params);
393
394                 (*context)->set_time(context+1,time,pos);
395         }
396 */
397 }
398
399 etl::handle<Layer>
400 Context::hit_check(const Point &pos)const
401 {
402         Context context(*this);
403
404         while(!context->empty())
405         {
406                 // If this layer is active, then go
407                 // ahead and break out of the loop
408                 if((*context)->active())
409                         break;
410
411                 // Otherwise, we want to keep searching
412                 // till we find either an active layer,
413                 // or the end of the layer list
414                 ++context;
415         }
416
417         // If this layer isn't defined, return an empty handle
418         if((context)->empty()) return 0;
419
420         return (*context)->hit_check(context+1, pos);
421 }