Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / 0.61.08 / src / synfig / target_scanline.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file target_scanline.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 "target_scanline.h"
34 #include "string.h"
35 #include "surface.h"
36 #include "render.h"
37 #include "canvas.h"
38 #include "context.h"
39
40 #endif
41
42 /* === U S I N G =========================================================== */
43
44 using namespace std;
45 using namespace etl;
46 using namespace synfig;
47
48 /* === M A C R O S ========================================================= */
49
50 #define SYNFIG_OPTIMIZE_LAYER_TREE
51
52 #define PIXEL_RENDERING_LIMIT 1500000
53
54 #define USE_PIXELRENDERING_LIMIT 1
55
56 /* === G L O B A L S ======================================================= */
57
58 /* === P R O C E D U R E S ================================================= */
59
60 /* === M E T H O D S ======================================================= */
61
62 Target_Scanline::Target_Scanline():
63         threads_(2)
64 {
65         curr_frame_=0;
66 }
67
68 int
69 Target_Scanline::next_frame(Time& time)
70 {
71         int
72                 total_frames(1),
73                 frame_start(0),
74                 frame_end(0);
75         Time
76                 time_start(0),
77                 time_end(0);
78
79         // If the description's end frame is equal to
80         // the start frame, then it is assumed that we
81         // are rendering only one frame. Correct it.
82         if(desc.get_frame_end()==desc.get_frame_start())
83                 desc.set_frame_end(desc.get_frame_start()+1);
84
85         frame_start=desc.get_frame_start();
86         frame_end=desc.get_frame_end();
87         time_start=desc.get_time_start();
88         time_end=desc.get_time_end();
89
90         // Calculate the number of frames
91         total_frames=frame_end-frame_start;
92         if(total_frames<=0)total_frames=1;
93
94         //RendDesc rend_desc=desc;
95         //rend_desc.set_gamma(1);
96
97 //      int total_tiles(total_tiles());
98         time=(time_end-time_start)*curr_frame_/total_frames+time_start;
99         curr_frame_++;
100
101 /*      synfig::info("curr_frame_: %d",curr_frame_);
102         synfig::info("total_frames: %d",total_frames);
103         synfig::info("time_end: %s",time_end.get_string().c_str());
104         synfig::info("time_start: %s",time_start.get_string().c_str());
105 */
106 //      synfig::info("time: %s",time.get_string().c_str());
107
108         return total_frames- curr_frame_+1;
109 }
110 bool
111 synfig::Target_Scanline::render(ProgressCallback *cb)
112 {
113         SuperCallback super_cb;
114         int
115 //              i=0,
116                 total_frames,
117                 quality=get_quality(),
118                 frame_start,
119                 frame_end;
120         Time
121                 t=0,
122                 time_start,
123                 time_end;
124
125         assert(canvas);
126         curr_frame_=0;
127
128         if( !init() ){
129                 if(cb) cb->error(_("Target initialization failure"));
130                 return false;
131         }
132
133         // If the description's end frame is equal to
134         // the start frame, then it is assumed that we
135         // are rendering only one frame. Correct it.
136         if(desc.get_frame_end()==desc.get_frame_start())
137                 desc.set_frame_end(desc.get_frame_start()+1);
138
139         frame_start=desc.get_frame_start();
140         frame_end=desc.get_frame_end();
141         time_start=desc.get_time_start();
142         time_end=desc.get_time_end();
143
144         // Calculate the number of frames
145         total_frames=frame_end-frame_start;
146
147
148         //RendDesc rend_desc=desc;
149
150         try {
151         // Grab the time
152         int i=next_frame(t);
153
154         //synfig::info("1time_set_to %s",t.get_string().c_str());
155
156         if(i>1)
157         do{
158
159         //if(total_frames>1)
160         //for(i=0,t=time_start;i<total_frames;i++)
161         //{
162                 //t=((time_end-time_start)*((Real)i/(Real)total_frames)).round(desc.get_frame_rate())+time_start;
163
164                 // If we have a callback, and it returns
165                 // false, go ahead and bail. (it may be a user cancel)
166                 if(cb && !cb->amount_complete(total_frames-(i-1),total_frames))
167                         return false;
168
169                 // Set the time that we wish to render
170                 if(!get_avoid_time_sync() || canvas->get_time()!=t)
171                         canvas->set_time(t);
172
173                 Context context;
174
175 #ifdef SYNFIG_OPTIMIZE_LAYER_TREE
176                 Canvas::Handle op_canvas;
177                 if (!getenv("SYNFIG_DISABLE_OPTIMIZE_LAYER_TREE"))
178                 {
179                         op_canvas = Canvas::create();
180                         op_canvas->set_file_name(canvas->get_file_name());
181                         optimize_layers(canvas->get_time(), canvas->get_context(), op_canvas);
182                         context=op_canvas->get_context();
183                 }
184                 else
185                         context=canvas->get_context();
186 #else
187                 context=canvas->get_context();
188 #endif
189
190                 // If the quality is set to zero, then we
191                 // use the parametric scanline-renderer.
192                 if(quality==0)
193                 {
194                         if(threads_<=0)
195                         {
196                                 if(!synfig::render(context,this,desc,0))
197                                         return false;
198                         }
199                         else
200                         {
201                                 if(!synfig::render_threaded(context,this,desc,0,threads_))
202                                         return false;
203                         }
204                 }
205                 else // If quality is set otherwise, then we use the accelerated renderer
206                 {
207                         #if USE_PIXELRENDERING_LIMIT
208                         if(desc.get_w()*desc.get_h() > PIXEL_RENDERING_LIMIT)
209                         {
210                                 Surface surface;
211                                 int rowheight = PIXEL_RENDERING_LIMIT/desc.get_w();
212                                 if (!rowheight) rowheight = 1; // TODO: render partial lines to stay within the limit?
213                                 int rows = desc.get_h()/rowheight;
214                                 int lastrowheight = desc.get_h() - rows*rowheight;
215
216                                 rows++;
217
218                                 synfig::info("Render broken up into %d block%s %d pixels tall, and a final block %d pixels tall",
219                                                          rows-1, rows==2?"":"s", rowheight, lastrowheight);
220
221                                 // loop through all the full rows
222                                 if(!start_frame())
223                                 {
224                                         throw(string("add_frame(): target panic on start_frame()"));
225                                         return false;
226                                 }
227
228                                 for(int i=0; i < rows; ++i)
229                                 {
230                                         RendDesc        blockrd = desc;
231
232                                         //render the strip at the normal size unless it's the last one...
233                                         if(i == rows-1)
234                                         {
235                                                 if(!lastrowheight) break;
236                                                 blockrd.set_subwindow(0,i*rowheight,desc.get_w(),lastrowheight);
237                                         }
238                                         else
239                                         {
240                                                 blockrd.set_subwindow(0,i*rowheight,desc.get_w(),rowheight);
241                                         }
242
243                                         if(!context.accelerated_render(&surface,quality,blockrd,0))
244                                         {
245                                                 if(cb)cb->error(_("Accelerated Renderer Failure"));
246                                                 return false;
247                                         }else
248                                         {
249                                                 int y;
250                                                 int rowspan=sizeof(Color)*surface.get_w();
251                                                 Surface::pen pen = surface.begin();
252
253                                                 int yoff = i*rowheight;
254
255                                                 for(y = 0; y < blockrd.get_h(); y++, pen.inc_y())
256                                                 {
257                                                         Color *colordata= start_scanline(y + yoff);
258                                                         if(!colordata)
259                                                         {
260                                                                 throw(string("add_frame(): call to start_scanline(y) returned NULL"));
261                                                                 return false;
262                                                         }
263
264                                                         if(get_remove_alpha())
265                                                         {
266                                                                 for(int i = 0; i < surface.get_w(); i++)
267                                                                         colordata[i] = Color::blend(surface[y][i],desc.get_bg_color(),1.0f);
268                                                         }
269                                                         else
270                                                                 memcpy(colordata,surface[y],rowspan);
271
272                                                         if(!end_scanline())
273                                                         {
274                                                                 throw(string("add_frame(): target panic on end_scanline()"));
275                                                                 return false;
276                                                         }
277                                                 }
278                                         }
279                                 }
280
281                                 end_frame();
282
283                         }else //use normal rendering...
284                         {
285                         #endif
286                                 Surface surface;
287
288                                 if(!context.accelerated_render(&surface,quality,desc,0))
289                                 {
290                                         // For some reason, the accelerated renderer failed.
291                                         if(cb)cb->error(_("Accelerated Renderer Failure"));
292                                         return false;
293                                 }
294                                 else
295                                 {
296                                         // Put the surface we renderer
297                                         // onto the target.
298                                         if(!add_frame(&surface))
299                                         {
300                                                 if(cb)cb->error(_("Unable to put surface on target"));
301                                                 return false;
302                                         }
303                                 }
304                         #if USE_PIXELRENDERING_LIMIT
305                         }
306                         #endif
307                 }
308         }while((i=next_frame(t)));
309     else
310     {
311                 // Set the time that we wish to render
312                 if(!get_avoid_time_sync() || canvas->get_time()!=t)
313                         canvas->set_time(t);
314                 Context context;
315
316 #ifdef SYNFIG_OPTIMIZE_LAYER_TREE
317                 Canvas::Handle op_canvas;
318                 if (!getenv("SYNFIG_DISABLE_OPTIMIZE_LAYER_TREE"))
319                 {
320                         op_canvas = Canvas::create();
321                         op_canvas->set_file_name(canvas->get_file_name());
322                         optimize_layers(canvas->get_time(), canvas->get_context(), op_canvas);
323                         context=op_canvas->get_context();
324                 }
325                 else
326                         context=canvas->get_context();
327 #else
328                 context=canvas->get_context();
329 #endif
330
331                 // If the quality is set to zero, then we
332                 // use the parametric scanline-renderer.
333                 if(quality==0)
334                 {
335                         if(threads_<=0)
336                         {
337                                 if(!synfig::render(context,this,desc,cb))
338                                         return false;
339                         }
340                         else
341                         {
342                                 if(!synfig::render_threaded(context,this,desc,cb,threads_))
343                                         return false;
344                         }
345                 }
346                 else // If quality is set otherwise, then we use the accelerated renderer
347                 {
348                         #if USE_PIXELRENDERING_LIMIT
349                         if(desc.get_w()*desc.get_h() > PIXEL_RENDERING_LIMIT)
350                         {
351                                 Surface surface;
352                                 int totalheight = desc.get_h();
353                                 int rowheight = PIXEL_RENDERING_LIMIT/desc.get_w();
354                                 if (!rowheight) rowheight = 1; // TODO: render partial lines to stay within the limit?
355                                 int rows = desc.get_h()/rowheight;
356                                 int lastrowheight = desc.get_h() - rows*rowheight;
357
358                                 rows++;
359
360                                 synfig::info("Render broken up into %d block%s %d pixels tall, and a final block %d pixels tall",
361                                                          rows-1, rows==2?"":"s", rowheight, lastrowheight);
362
363                                 // loop through all the full rows
364                                 if(!start_frame())
365                                 {
366                                         throw(string("add_frame(): target panic on start_frame()"));
367                                         return false;
368                                 }
369
370                                 for(int i=0; i < rows; ++i)
371                                 {
372                                         RendDesc        blockrd = desc;
373
374                                         //render the strip at the normal size unless it's the last one...
375                                         if(i == rows-1)
376                                         {
377                                                 if(!lastrowheight) break;
378                                                 blockrd.set_subwindow(0,i*rowheight,desc.get_w(),lastrowheight);
379                                         }
380                                         else
381                                         {
382                                                 blockrd.set_subwindow(0,i*rowheight,desc.get_w(),rowheight);
383                                         }
384
385                                         SuperCallback   sc(cb, i*rowheight, (i+1)*rowheight, totalheight);
386
387                                         if(!context.accelerated_render(&surface,quality,blockrd,&sc))
388                                         {
389                                                 if(cb)cb->error(_("Accelerated Renderer Failure"));
390                                                 return false;
391                                         }else
392                                         {
393                                                 int y;
394                                                 int rowspan=sizeof(Color)*surface.get_w();
395                                                 Surface::pen pen = surface.begin();
396
397                                                 int yoff = i*rowheight;
398
399                                                 for(y = 0; y < blockrd.get_h(); y++, pen.inc_y())
400                                                 {
401                                                         Color *colordata= start_scanline(y + yoff);
402                                                         if(!colordata)
403                                                         {
404                                                                 throw(string("add_frame(): call to start_scanline(y) returned NULL"));
405                                                                 return false;
406                                                         }
407
408                                                         if(get_remove_alpha())
409                                                         {
410                                                                 for(int i = 0; i < surface.get_w(); i++)
411                                                                         colordata[i] = Color::blend(surface[y][i],desc.get_bg_color(),1.0f);
412                                                         }
413                                                         else
414                                                                 memcpy(colordata,surface[y],rowspan);
415
416                                                         if(!end_scanline())
417                                                         {
418                                                                 throw(string("add_frame(): target panic on end_scanline()"));
419                                                                 return false;
420                                                         }
421                                                 }
422                                         }
423
424                                         //I'm done with this part
425                                         sc.amount_complete(100,100);
426                                 }
427
428                                 end_frame();
429
430                         }else
431                         {
432                         #endif
433                                 Surface surface;
434
435                                 if(!context.accelerated_render(&surface,quality,desc,cb))
436                                 {
437                                         if(cb)cb->error(_("Accelerated Renderer Failure"));
438                                         return false;
439                                 }
440                                 else
441                                 {
442                                         // Put the surface we renderer
443                                         // onto the target.
444                                         if(!add_frame(&surface))
445                                         {
446                                                 if(cb)cb->error(_("Unable to put surface on target"));
447                                                 return false;
448                                         }
449                                 }
450                         #if USE_PIXELRENDERING_LIMIT
451                         }
452                         #endif
453                 }
454         }
455
456         }
457         catch(String str)
458         {
459                 if(cb)cb->error(_("Caught string :")+str);
460                 return false;
461         }
462         catch(std::bad_alloc)
463         {
464                 if(cb)cb->error(_("Ran out of memory (Probably a bug)"));
465                 return false;
466         }
467         catch(...)
468         {
469                 if(cb)cb->error(_("Caught unknown error, rethrowing..."));
470                 throw;
471         }
472         return true;
473 }
474
475 bool
476 Target_Scanline::add_frame(const Surface *surface)
477 {
478         assert(surface);
479
480
481         int y;
482         int rowspan=sizeof(Color)*surface->get_w();
483         Surface::const_pen pen=surface->begin();
484
485         if(!start_frame())
486         {
487                 throw(string("add_frame(): target panic on start_frame()"));
488                 return false;
489         }
490
491         for(y=0;y<surface->get_h();y++,pen.inc_y())
492         {
493                 Color *colordata= start_scanline(y);
494                 if(!colordata)
495                 {
496                         throw(string("add_frame(): call to start_scanline(y) returned NULL"));
497                         return false;
498                 }
499
500                 if(get_remove_alpha())
501                 {
502                         for(int i=0;i<surface->get_w();i++)
503                                 colordata[i]=Color::blend((*surface)[y][i],desc.get_bg_color(),1.0f);
504                 }
505                 else
506                         memcpy(colordata,(*surface)[y],rowspan);
507
508                 if(!end_scanline())
509                 {
510                         throw(string("add_frame(): target panic on end_scanline()"));
511                         return false;
512                 }
513         }
514
515         end_frame();
516
517         return true;
518 }