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