9c000d50153905fae0ac4cf834e601b6c610f6c6
[synfig.git] / synfig-core / trunk / src / synfig / target_tile.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file target_tile.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) 2007 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 "target_tile.h"
34 #include "string.h"
35 #include "surface.h"
36 #include "render.h"
37 #include "canvas.h"
38 #include "context.h"
39 #include "general.h"
40 #include <ETL/clock>
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 const unsigned int      DEF_TILE_WIDTH = TILE_SIZE / 2;
52 const unsigned int      DEF_TILE_HEIGHT= TILE_SIZE / 2;
53
54 #define SYNFIG_OPTIMIZE_LAYER_TREE      1
55 #ifdef _DEBUG
56 // #define SYNFIG_DISPLAY_EFFICIENCY    1
57 #endif
58
59 /* === G L O B A L S ======================================================= */
60
61 /* === P R O C E D U R E S ================================================= */
62
63 /* === M E T H O D S ======================================================= */
64
65 Target_Tile::Target_Tile():
66         threads_(2),
67         tile_w_(DEF_TILE_WIDTH),
68         tile_h_(DEF_TILE_HEIGHT),
69         curr_tile_(0),
70         clipping_(true)
71 {
72         curr_frame_=0;
73 }
74
75 int
76 Target_Tile::next_frame(Time& time)
77 {
78         int
79                 total_frames(1),
80                 frame_start(0),
81                 frame_end(0);
82         Time
83                 time_start(0),
84                 time_end(0);
85
86         // If the description's end frame is equal to
87         // the start frame, then it is assumed that we
88         // are rendering only one frame. Correct it.
89         if(desc.get_frame_end()==desc.get_frame_start())
90                 desc.set_frame_end(desc.get_frame_start()+1);
91
92         frame_start=desc.get_frame_start();
93         frame_end=desc.get_frame_end();
94         time_start=desc.get_time_start();
95         time_end=desc.get_time_end();
96
97         // Calculate the number of frames
98         total_frames=frame_end-frame_start;
99         if(total_frames<=0)total_frames=1;
100
101         //RendDesc rend_desc=desc;
102         //rend_desc.set_gamma(1);
103
104 //      int total_tiles(total_tiles());
105         time=(time_end-time_start)*curr_frame_/total_frames+time_start;
106         curr_frame_++;
107
108 /*      synfig::info("curr_frame_: %d",curr_frame_);
109         synfig::info("total_frames: %d",total_frames);
110         synfig::info("time_end: %s",time_end.get_string().c_str());
111         synfig::info("time_start: %s",time_start.get_string().c_str());
112 */
113 //      synfig::info("time: %s",time.get_string().c_str());
114
115         return total_frames- curr_frame_+1;
116 }
117
118 int
119 Target_Tile::next_tile(int& x, int& y)
120 {
121         // Width of the image(in tiles)
122         int tw(rend_desc().get_w()/tile_w_);
123         int th(rend_desc().get_h()/tile_h_);
124
125         // Add the last tiles (which will be clipped)
126         if(rend_desc().get_w()%tile_w_!=0)tw++;
127         if(rend_desc().get_h()%tile_h_!=0)th++;
128
129         x=(curr_tile_%tw)*tile_h_;
130         y=(curr_tile_/tw)*tile_w_;
131
132         curr_tile_++;
133         return (tw*th)-curr_tile_+1;
134 }
135
136 bool
137 synfig::Target_Tile::render_frame_(Context context,ProgressCallback *cb)
138 {
139         if(tile_w_<=0||tile_h_<=0)
140         {
141                 if(cb)cb->error(_("Bad Tile Size"));
142                 return false;
143         }
144         const RendDesc &rend_desc(desc);
145 #define total_tiles total_tiles()
146
147         etl::clock total_time;
148         etl::clock::value_type work_time(0);
149         etl::clock::value_type find_tile_time(0);
150         etl::clock::value_type add_tile_time(0);
151         total_time.reset();
152
153         // If the quality is set to zero, then we
154         // use the parametric scanline-renderer.
155         if(get_quality()==0)
156         {
157                 Surface surface;
158
159                 RendDesc tile_desc;
160                 int x,y,w,h;
161                 int i;
162                 etl::clock tile_timer;
163                 tile_timer.reset();
164                 while((i=next_tile(x,y)))
165                 {
166                         find_tile_time+=tile_timer();
167                         SuperCallback   super(cb,(total_tiles-i+1)*1000,(total_tiles-i+2)*1000,total_tiles*1000);
168                         if(!super.amount_complete(0,1000))
169                                 return false;
170                         //if(cb && !cb->amount_complete(total_tiles-i,total_tiles))
171                         //      return false;
172
173                         // Perform clipping on the tile
174                         if(clipping_)
175                         {
176                                 w=x+tile_w_<rend_desc.get_w()?tile_w_:rend_desc.get_w()-x;
177                                 h=y+tile_h_<rend_desc.get_h()?tile_h_:rend_desc.get_h()-y;
178                                 if(w<=0||h<=0)continue;
179                         }
180                         else
181                         {
182                                 w=tile_w_;
183                                 h=tile_h_;
184                         }
185
186                         tile_desc=rend_desc;
187                         tile_desc.set_subwindow(x,y,w,h);
188                         if(!parametric_render(context, surface, tile_desc,&super))
189                         {
190                                 // For some reason, the parametric renderer failed.
191                                 if(cb)cb->error(_("Parametric Renderer Failure"));
192                                 return false;
193                         }
194                         else
195                         {
196                                 if(!surface)
197                                 {
198                                         if(cb)cb->error(_("Bad surface"));
199                                         return false;
200                                 }
201                                 if(get_remove_alpha())
202                                         for(int i=0;i<surface.get_w()*surface.get_h();i++)
203                                                 surface[0][i]=Color::blend(surface[0][i],desc.get_bg_color(),1.0f);
204
205                                 // Add the tile to the target
206                                 if(!add_tile(surface,x,y))
207                                 {
208                                         if(cb)cb->error(_("add_tile():Unable to put surface on target"));
209                                         return false;
210                                 }
211                         }
212                 tile_timer.reset();
213                 }
214         }
215         else // If quality is set otherwise, then we use the accelerated renderer
216         {
217                 Surface surface;
218
219                 RendDesc tile_desc;
220                 int x,y,w,h;
221                 int i;
222                 etl::clock tile_timer;
223                 tile_timer.reset();
224                 while((i=next_tile(x,y)))
225                 {
226                         find_tile_time+=tile_timer();
227                         SuperCallback   super(cb,(total_tiles-i)*1000,(total_tiles-i+1)*1000,total_tiles*1000);
228                         if(!super.amount_complete(0,1000))
229                                 return false;
230 //                      if(cb && !cb->amount_complete(total_tiles-i,total_tiles))
231 //                              return false;
232                         // Perform clipping on the tile
233                         if(clipping_)
234                         {
235                                 w=x+tile_w_<rend_desc.get_w()?tile_w_:rend_desc.get_w()-x;
236                                 h=y+tile_h_<rend_desc.get_h()?tile_h_:rend_desc.get_h()-y;
237                                 if(w<=0||h<=0)continue;
238                         }
239                         else
240                         {
241                                 w=tile_w_;
242                                 h=tile_h_;
243                         }
244
245                         tile_desc=rend_desc;
246                         tile_desc.set_subwindow(x,y,w,h);
247
248                         etl::clock timer2;
249                         timer2.reset();
250
251                         if(!context.accelerated_render(&surface,get_quality(),tile_desc,&super))
252                         {
253                                 // For some reason, the accelerated renderer failed.
254                                 if(cb)cb->error(_("Accelerated Renderer Failure"));
255                                 return false;
256                         }
257                         else
258                         {
259                                 work_time+=timer2();
260                                 if(!surface)
261                                 {
262                                         if(cb)cb->error(_("Bad surface"));
263                                         return false;
264                                 }
265                                 if(get_remove_alpha())
266                                         for(int i=0;i<surface.get_w()*surface.get_h();i++)
267                                                 surface[0][i]=Color::blend(surface[0][i],desc.get_bg_color(),1.0f);
268
269                                 etl::clock timer;
270                                 timer.reset();
271                                 // Add the tile to the target
272                                 if(!add_tile(surface,x,y))
273                                 {
274                                         if(cb)cb->error(_("add_tile():Unable to put surface on target"));
275                                         return false;
276                                 }
277                                 add_tile_time+=timer();
278                         }
279                         tile_timer.reset();
280                         signal_progress()();
281                 }
282         }
283         if(cb && !cb->amount_complete(total_tiles,total_tiles))
284                 return false;
285
286 #if SYNFIG_DISPLAY_EFFICIENCY==1
287         synfig::info(">>>>>> Render Time: %fsec, Find Tile Time: %fsec, Add Tile Time: %fsec, Total Time: %fsec",work_time,find_tile_time,add_tile_time,total_time());
288         synfig::info(">>>>>> FRAME EFFICIENCY: %f%%",(100.0f*work_time/total_time()));
289 #endif
290 #undef total_tiles
291         return true;
292 }
293
294 bool
295 synfig::Target_Tile::render(ProgressCallback *cb)
296 {
297         SuperCallback super_cb;
298         int
299                 i=0,
300                 total_frames,
301                 frame_start,
302                 frame_end;
303         Time
304                 t=0,
305                 time_start,
306                 time_end;
307
308         assert(canvas);
309         curr_frame_=0;
310         init();
311         if( !init() ){
312                 if(cb) cb->error(_("Target initialization failure"));
313                 return false;
314         }
315
316
317         // If the description's end frame is equal to
318         // the start frame, then it is assumed that we
319         // are rendering only one frame. Correct it.
320         if(desc.get_frame_end()==desc.get_frame_start())
321                 desc.set_frame_end(desc.get_frame_start()+1);
322
323         frame_start=desc.get_frame_start();
324         frame_end=desc.get_frame_end();
325         time_start=desc.get_time_start();
326         time_end=desc.get_time_end();
327
328         // Calculate the number of frames
329         total_frames=frame_end-frame_start;
330
331
332
333         try {
334                 // Grab the time
335                 i=next_frame(t);
336
337                 //synfig::info("1time_set_to %s",t.get_string().c_str());
338
339                 if(i>=1)
340                 {
341                 do
342                 {
343                         curr_tile_=0;
344
345                         // If we have a callback, and it returns
346                         // false, go ahead and bail. (maybe a use cancel)
347                         if(cb && !cb->amount_complete(total_frames-(i-1),total_frames))
348                                 return false;
349
350                         if(!start_frame(cb))
351                                 return false;
352
353                         // Set the time that we wish to render
354                         //if(!get_avoid_time_sync() || canvas->get_time()!=t)
355                                 canvas->set_time(t);
356
357                         Context context;
358
359                         #ifdef SYNFIG_OPTIMIZE_LAYER_TREE
360                         Canvas::Handle op_canvas(Canvas::create());
361                         op_canvas->set_file_name(canvas->get_file_name());
362                         optimize_layers(canvas->get_time(), canvas->get_context(), op_canvas);
363                         context=op_canvas->get_context();
364                         #else
365                         context=canvas->get_context();
366                         #endif
367
368 /*
369                         #ifdef SYNFIG_OPTIMIZE_LAYER_TREE
370                         Context context;
371                         Canvas::Handle op_canvas(Canvas::create());
372                         op_canvas->set_file_name(canvas->get_file_name());
373                         // Set the time that we wish to render
374                         canvas->set_time(t);
375                         optimize_layers(canvas->get_time(), canvas->get_context(), op_canvas);
376                         context=op_canvas->get_context();
377                         #else
378                         Context context;
379                         // Set the time that we wish to render
380                         canvas->set_time(t);
381                         context=canvas->get_context();
382                         #endif
383 */
384
385                         if(!render_frame_(context,0))
386                                 return false;
387                         end_frame();
388                 }while((i=next_frame(t)));
389                 //synfig::info("tilerenderer: i=%d, t=%s",i,t.get_string().c_str());
390                 }
391                 else
392                 {
393                         curr_tile_=0;
394
395                         if(!start_frame(cb))
396                                 return false;
397
398                         // Set the time that we wish to render
399 //                      if(!get_avoid_time_sync() || canvas->get_time()!=t)
400                                 canvas->set_time(t);
401
402                         //synfig::info("2time_set_to %s",t.get_string().c_str());
403
404                         Context context;
405
406                         #ifdef SYNFIG_OPTIMIZE_LAYER_TREE
407                         Canvas::Handle op_canvas(Canvas::create());
408                         op_canvas->set_file_name(canvas->get_file_name());
409                         optimize_layers(canvas->get_time(), canvas->get_context(), op_canvas);
410                         context=op_canvas->get_context();
411                         #else
412                         context=canvas->get_context();
413                         #endif
414
415                         if(!render_frame_(context, cb))
416                                 return false;
417                         end_frame();
418                 }
419
420         }
421         catch(String str)
422         {
423                 if(cb)cb->error(_("Caught string :")+str);
424                 return false;
425         }
426         catch(std::bad_alloc)
427         {
428                 if(cb)cb->error(_("Ran out of memory (Probably a bug)"));
429                 return false;
430         }
431         catch(...)
432         {
433                 if(cb)cb->error(_("Caught unknown error, rethrowing..."));
434                 throw;
435         }
436         return true;
437 }