First attempt at fixing the 'straight' blend method. It now applies to the whole...
[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                                 composite->get_blend_method() == Color::BLEND_STRAIGHT &&
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 is 1.0
215                 // then we don't want to render the context
216                 if (composite && composite->get_blend_method() == Color::BLEND_STRAIGHT &&
217                         composite->get_amount() == 1.0f)
218                 {
219                         Layer::Handle layer = *context;
220                         while (!context->empty()) context++; // skip the context
221                         return layer->accelerated_render(context,surface,quality,renddesc, cb);
222                 }
223
224                 // Break out of the loop--we have found a good layer
225                 break;
226         }
227
228         // If this layer isn't defined, return alpha
229         if (context->empty() || (straight_and_empty && composite->get_amount() == 1.0f))
230         {
231 #ifdef SYNFIG_DEBUG_LAYERS
232                 synfig::info("Context::accelerated_render(): Hit end of list");
233 #endif  // SYNFIG_DEBUG_LAYERS
234                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
235                 surface->clear();
236 #ifdef SYNFIG_PROFILE_LAYERS
237                 profile_timer.reset();
238 #endif  // SYNFIG_PROFILE_LAYERS
239                 return true;
240         }
241
242 #ifdef SYNFIG_DEBUG_LAYERS
243         synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
244 #endif  // SYNFIG_DEBUG_LAYERS
245
246         try {
247                 RWLock::ReaderLock lock((*context)->get_rw_lock());
248
249 #ifdef SYNFIG_PROFILE_LAYERS
250         //go down one layer :P
251         depth++;
252         curr_layer=(*context)->get_name();      //make sure the layer inside is referring to the correct layer outside
253         profile_timer.reset();                                                                          // +
254 #endif  // SYNFIG_PROFILE_LAYERS
255
256         bool ret;
257
258         // this layer doesn't draw anything onto the canvas we're
259         // rendering, but it uses straight blending, so we need to render
260         // the stuff under us and then blit transparent pixels over it
261         // using the appropriate 'amount'
262         if (straight_and_empty)
263         {
264                 if (ret = Context((context+1)).accelerated_render(surface,quality,renddesc,cb))
265                 {
266                         Surface clearsurface;
267                         clearsurface.set_wh(renddesc.get_w(),renddesc.get_h());
268                         clearsurface.clear();
269
270                         Surface::alpha_pen apen(surface->begin());
271                         apen.set_alpha(composite->get_amount());
272                         apen.set_blend_method(Color::BLEND_STRAIGHT);
273
274                         clearsurface.blit_to(apen);
275                 }
276         }
277         else
278                 ret = (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
279
280 #ifdef SYNFIG_PROFILE_LAYERS
281         //post work for the previous layer
282         time_table[curr_layer]+=profile_timer();                                                        //-
283         if(run_table.count(curr_layer))run_table[curr_layer]++;
284                 else run_table[curr_layer]=1;
285
286         depth--;
287         curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
288
289         //print out the table it we're done...
290         if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
291         profile_timer.reset();                                                                                          //+
292 #endif  // SYNFIG_PROFILE_LAYERS
293
294         return ret;
295         }
296         catch(std::bad_alloc)
297         {
298                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
299 #ifdef _DEBUG
300                 return false;
301 #else  // _DEBUG
302                 ++context;
303                 return context.accelerated_render(surface, quality, renddesc, cb);
304 #endif  // _DEBUG
305         }
306         catch(...)
307         {
308                 synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
309                 throw;
310         }
311 }
312
313 void
314 Context::set_time(Time time)const
315 {
316         Context context(*this);
317         while(!(context)->empty())
318         {
319                 // If this layer is active, then go
320                 // ahead and break out of the loop
321                 if((*context)->active() && !(*context)->dirty_time_.is_equal(time))
322                         break;
323
324                 // Otherwise, we want to keep searching
325                 // till we find either an active layer,
326                 // or the end of the layer list
327                 ++context;
328         }
329
330         // If this layer isn't defined, just return
331         if((context)->empty()) return;
332
333         // Set up a writer lock
334         RWLock::WriterLock lock((*context)->get_rw_lock());
335
336         //synfig::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
337         //synfig::info("%s: time=%f",(*context)->get_name().c_str(),(float)time);
338
339         {
340                 Layer::ParamList params;
341                 Layer::DynamicParamList::const_iterator iter;
342
343                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
344                         params[iter->first]=(*iter->second)(time);
345
346                 (*context)->set_param_list(params);
347
348                 (*context)->set_time(context+1,time);
349                 (*context)->dirty_time_=time;
350
351         }
352 }
353
354 void
355 Context::set_time(Time time,const Vector &/*pos*/)const
356 {
357         set_time(time);
358 /*
359         Context context(*this);
360         while(!(context)->empty())
361         {
362                 // If this layer is active, then go
363                 // ahead and break out of the loop
364                 if((*context)->active())
365                         break;
366
367                 // Otherwise, we want to keep searching
368                 // till we find either an active layer,
369                 // or the end of the layer list
370                 ++context;
371         }
372
373         // If this layer isn't defined, just return
374         if((context)->empty()) return;
375
376         else
377         {
378                 Layer::ParamList params;
379                 Layer::DynamicParamList::const_iterator iter;
380
381                 for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
382                         params[iter->first]=(*iter->second)(time);
383
384                 (*context)->set_param_list(params);
385
386                 (*context)->set_time(context+1,time,pos);
387         }
388 */
389 }
390
391 etl::handle<Layer>
392 Context::hit_check(const Point &pos)const
393 {
394         Context context(*this);
395
396         while(!context->empty())
397         {
398                 // If this layer is active, then go
399                 // ahead and break out of the loop
400                 if((*context)->active())
401                         break;
402
403                 // Otherwise, we want to keep searching
404                 // till we find either an active layer,
405                 // or the end of the layer list
406                 ++context;
407         }
408
409         // If this layer isn't defined, return an empty handle
410         if((context)->empty()) return 0;
411
412         return (*context)->hit_check(context+1, pos);
413 }