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