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