Added copyright lines for files I've edited this year.
[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 #define SYNFIG_OPTIMIZE_LAYER_TREE 1
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(Canvas::create());
177                 op_canvas->set_file_name(canvas->get_file_name());
178                 optimize_layers(canvas->get_time(), canvas->get_context(), op_canvas);
179                 context=op_canvas->get_context();
180                 #else
181                 context=canvas->get_context();
182                 #endif
183
184                 // If the quality is set to zero, then we
185                 // use the parametric scanline-renderer.
186                 if(quality==0)
187                 {
188                         if(threads_<=0)
189                         {
190                                 if(!synfig::render(context,this,desc,0))
191                                         return false;
192                         }
193                         else
194                         {
195                                 if(!synfig::render_threaded(context,this,desc,0,threads_))
196                                         return false;
197                         }
198                 }
199                 else // If quality is set otherwise, then we use the accelerated renderer
200                 {
201                         #if USE_PIXELRENDERING_LIMIT
202                         if(desc.get_w()*desc.get_h() > PIXEL_RENDERING_LIMIT)
203                         {
204                                 Surface surface;
205                                 int rowheight = PIXEL_RENDERING_LIMIT/desc.get_w();
206                                 if (!rowheight) rowheight = 1; // TODO: render partial lines to stay within the limit?
207                                 int rows = desc.get_h()/rowheight;
208                                 int lastrowheight = desc.get_h() - rows*rowheight;
209
210                                 rows++;
211
212                                 synfig::info("Render broken up into %d block%s %d pixels tall, and a final block %d pixels tall",
213                                                          rows-1, rows==2?"":"s", rowheight, lastrowheight);
214
215                                 // loop through all the full rows
216                                 if(!start_frame())
217                                 {
218                                         throw(string("add_frame(): target panic on start_frame()"));
219                                         return false;
220                                 }
221
222                                 for(int i=0; i < rows; ++i)
223                                 {
224                                         RendDesc        blockrd = desc;
225
226                                         //render the strip at the normal size unless it's the last one...
227                                         if(i == rows-1)
228                                         {
229                                                 if(!lastrowheight) break;
230                                                 blockrd.set_subwindow(0,i*rowheight,desc.get_w(),lastrowheight);
231                                         }
232                                         else
233                                         {
234                                                 blockrd.set_subwindow(0,i*rowheight,desc.get_w(),rowheight);
235                                         }
236
237                                         if(!context.accelerated_render(&surface,quality,blockrd,0))
238                                         {
239                                                 if(cb)cb->error(_("Accelerated Renderer Failure"));
240                                                 return false;
241                                         }else
242                                         {
243                                                 int y;
244                                                 int rowspan=sizeof(Color)*surface.get_w();
245                                                 Surface::pen pen = surface.begin();
246
247                                                 int yoff = i*rowheight;
248
249                                                 for(y = 0; y < blockrd.get_h(); y++, pen.inc_y())
250                                                 {
251                                                         Color *colordata= start_scanline(y + yoff);
252                                                         if(!colordata)
253                                                         {
254                                                                 throw(string("add_frame(): call to start_scanline(y) returned NULL"));
255                                                                 return false;
256                                                         }
257
258                                                         if(get_remove_alpha())
259                                                         {
260                                                                 for(int i = 0; i < surface.get_w(); i++)
261                                                                         colordata[i] = Color::blend(surface[y][i],desc.get_bg_color(),1.0f);
262                                                         }
263                                                         else
264                                                                 memcpy(colordata,surface[y],rowspan);
265
266                                                         if(!end_scanline())
267                                                         {
268                                                                 throw(string("add_frame(): target panic on end_scanline()"));
269                                                                 return false;
270                                                         }
271                                                 }
272                                         }
273                                 }
274
275                                 end_frame();
276
277                         }else //use normal rendering...
278                         {
279                         #endif
280                                 Surface surface;
281
282                                 if(!context.accelerated_render(&surface,quality,desc,0))
283                                 {
284                                         // For some reason, the accelerated renderer failed.
285                                         if(cb)cb->error(_("Accelerated Renderer Failure"));
286                                         return false;
287                                 }
288                                 else
289                                 {
290                                         // Put the surface we renderer
291                                         // onto the target.
292                                         if(!add_frame(&surface))
293                                         {
294                                                 if(cb)cb->error(_("Unable to put surface on target"));
295                                                 return false;
296                                         }
297                                 }
298                         #if USE_PIXELRENDERING_LIMIT
299                         }
300                         #endif
301                 }
302         }while((i=next_frame(t)));
303     else
304     {
305                 // Set the time that we wish to render
306                 if(!get_avoid_time_sync() || canvas->get_time()!=t)
307                         canvas->set_time(t);
308                 Context context;
309
310                 #ifdef SYNFIG_OPTIMIZE_LAYER_TREE
311                 Canvas::Handle op_canvas(Canvas::create());
312                 op_canvas->set_file_name(canvas->get_file_name());
313                 optimize_layers(canvas->get_time(), canvas->get_context(), op_canvas);
314                 context=op_canvas->get_context();
315                 #else
316                 context=canvas->get_context();
317                 #endif
318
319                 // If the quality is set to zero, then we
320                 // use the parametric scanline-renderer.
321                 if(quality==0)
322                 {
323                         if(threads_<=0)
324                         {
325                                 if(!synfig::render(context,this,desc,cb))
326                                         return false;
327                         }
328                         else
329                         {
330                                 if(!synfig::render_threaded(context,this,desc,cb,threads_))
331                                         return false;
332                         }
333                 }
334                 else // If quality is set otherwise, then we use the accelerated renderer
335                 {
336                         #if USE_PIXELRENDERING_LIMIT
337                         if(desc.get_w()*desc.get_h() > PIXEL_RENDERING_LIMIT)
338                         {
339                                 Surface surface;
340                                 int totalheight = desc.get_h();
341                                 int rowheight = PIXEL_RENDERING_LIMIT/desc.get_w();
342                                 if (!rowheight) rowheight = 1; // TODO: render partial lines to stay within the limit?
343                                 int rows = desc.get_h()/rowheight;
344                                 int lastrowheight = desc.get_h() - rows*rowheight;
345
346                                 rows++;
347
348                                 synfig::info("Render broken up into %d block%s %d pixels tall, and a final block %d pixels tall",
349                                                          rows-1, rows==2?"":"s", rowheight, lastrowheight);
350
351                                 // loop through all the full rows
352                                 if(!start_frame())
353                                 {
354                                         throw(string("add_frame(): target panic on start_frame()"));
355                                         return false;
356                                 }
357
358                                 for(int i=0; i < rows; ++i)
359                                 {
360                                         RendDesc        blockrd = desc;
361
362                                         //render the strip at the normal size unless it's the last one...
363                                         if(i == rows-1)
364                                         {
365                                                 if(!lastrowheight) break;
366                                                 blockrd.set_subwindow(0,i*rowheight,desc.get_w(),lastrowheight);
367                                         }
368                                         else
369                                         {
370                                                 blockrd.set_subwindow(0,i*rowheight,desc.get_w(),rowheight);
371                                         }
372
373                                         SuperCallback   sc(cb, i*rowheight, (i+1)*rowheight, totalheight);
374
375                                         if(!context.accelerated_render(&surface,quality,blockrd,&sc))
376                                         {
377                                                 if(cb)cb->error(_("Accelerated Renderer Failure"));
378                                                 return false;
379                                         }else
380                                         {
381                                                 int y;
382                                                 int rowspan=sizeof(Color)*surface.get_w();
383                                                 Surface::pen pen = surface.begin();
384
385                                                 int yoff = i*rowheight;
386
387                                                 for(y = 0; y < blockrd.get_h(); y++, pen.inc_y())
388                                                 {
389                                                         Color *colordata= start_scanline(y + yoff);
390                                                         if(!colordata)
391                                                         {
392                                                                 throw(string("add_frame(): call to start_scanline(y) returned NULL"));
393                                                                 return false;
394                                                         }
395
396                                                         if(get_remove_alpha())
397                                                         {
398                                                                 for(int i = 0; i < surface.get_w(); i++)
399                                                                         colordata[i] = Color::blend(surface[y][i],desc.get_bg_color(),1.0f);
400                                                         }
401                                                         else
402                                                                 memcpy(colordata,surface[y],rowspan);
403
404                                                         if(!end_scanline())
405                                                         {
406                                                                 throw(string("add_frame(): target panic on end_scanline()"));
407                                                                 return false;
408                                                         }
409                                                 }
410                                         }
411
412                                         //I'm done with this part
413                                         sc.amount_complete(100,100);
414                                 }
415
416                                 end_frame();
417
418                         }else
419                         {
420                         #endif
421                                 Surface surface;
422
423                                 if(!context.accelerated_render(&surface,quality,desc,cb))
424                                 {
425                                         if(cb)cb->error(_("Accelerated Renderer Failure"));
426                                         return false;
427                                 }
428                                 else
429                                 {
430                                         // Put the surface we renderer
431                                         // onto the target.
432                                         if(!add_frame(&surface))
433                                         {
434                                                 if(cb)cb->error(_("Unable to put surface on target"));
435                                                 return false;
436                                         }
437                                 }
438                         #if USE_PIXELRENDERING_LIMIT
439                         }
440                         #endif
441                 }
442         }
443
444         }
445         catch(String str)
446         {
447                 if(cb)cb->error(_("Caught string :")+str);
448                 return false;
449         }
450         catch(std::bad_alloc)
451         {
452                 if(cb)cb->error(_("Ran out of memory (Probably a bug)"));
453                 return false;
454         }
455         catch(...)
456         {
457                 if(cb)cb->error(_("Caught unknown error, rethrowing..."));
458                 throw;
459         }
460         return true;
461 }
462
463 bool
464 Target_Scanline::add_frame(const Surface *surface)
465 {
466         assert(surface);
467
468
469         int y;
470         int rowspan=sizeof(Color)*surface->get_w();
471         Surface::const_pen pen=surface->begin();
472
473         if(!start_frame())
474         {
475                 throw(string("add_frame(): target panic on start_frame()"));
476                 return false;
477         }
478
479         for(y=0;y<surface->get_h();y++,pen.inc_y())
480         {
481                 Color *colordata= start_scanline(y);
482                 if(!colordata)
483                 {
484                         throw(string("add_frame(): call to start_scanline(y) returned NULL"));
485                         return false;
486                 }
487
488                 if(get_remove_alpha())
489                 {
490                         for(int i=0;i<surface->get_w();i++)
491                                 colordata[i]=Color::blend((*surface)[y][i],desc.get_bg_color(),1.0f);
492                 }
493                 else
494                         memcpy(colordata,(*surface)[y],rowspan);
495
496                 if(!end_scanline())
497                 {
498                         throw(string("add_frame(): target panic on end_scanline()"));
499                         return false;
500                 }
501         }
502
503         end_frame();
504
505         return true;
506 }