Enabled $Id$ expansion.
[synfig.git] / synfig-core / trunk / src / synfig / render.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file synfig/render.cpp
3 **      \brief Renderer
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 #define SYNFIG_NO_ANGLE
26
27 #ifdef USING_PCH
28 #       include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #       include <config.h>
32 #endif
33
34 #ifndef WIN32
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <signal.h>
38 #endif
39
40 #include "render.h"
41 #include "target.h"
42 #include "canvas.h"
43 #include <ETL/handle>
44 #include <cassert>
45 #include "context.h"
46 #include "surface.h"
47
48 #endif
49
50 using namespace std;
51 using namespace synfig;
52 using namespace etl;
53
54 /* === M A C R O S ========================================================= */
55
56 /* === P R O C E D U R E S ================================================= */
57
58 bool
59 synfig::parametric_render(
60         Context context,
61         Surface &surface,
62         const RendDesc &desc,
63         ProgressCallback *callback
64 )
65 {
66         Point::value_type
67                 u,v,            // Current location in image
68                 su,sv,          // Starting locations
69                 du, dv,         // Distance between pixels
70                 dsu,dsv;        // Distance between subpixels
71
72         bool
73                 no_clamp=!desc.get_clamp();
74
75         int
76                 w(desc.get_w()),
77                 h(desc.get_h()),
78                 a(desc.get_antialias());
79
80         Point
81                 tl(desc.get_tl()),
82                 br(desc.get_br());
83
84         //Gamma
85         //      gamma(desc.get_gamma());
86
87         int
88                 x,y,            // Current location on output bitmap
89                 x2,y2;          // Subpixel counters
90
91         Color::value_type
92                 pool;           // Alpha pool (for correct alpha antialiasing)
93
94         // Calculate the number of channels
95         //chan=channels(desc.get_pixel_format());
96
97         // Calculate the distance between pixels
98         du=(br[0]-tl[0])/(Point::value_type)w;
99         dv=(br[1]-tl[1])/(Point::value_type)h;
100
101         // Calculate the distance between sub pixels
102         dsu=du/(Point::value_type)a;
103         dsv=dv/(Point::value_type)a;
104
105         // Calculate the starting points
106         //su=tl[0]+(du-dsu)/(Point::value_type)2.0;
107         //sv=tl[1]-(dv-dsv)/(Point::value_type)2.0;
108         su=tl[0];
109         sv=tl[1];
110
111         surface.set_wh(desc.get_w(),desc.get_h());
112
113         assert(surface);
114
115         // Loop through all horizontal lines
116         for(y=0,v=sv;y<h;y++,v+=dv)
117         {
118                 // Set the current pixel pointer
119                 // to the start of the line
120                 Color *colordata=surface[y];
121
122                 assert(colordata);
123
124                 // If we have a callback that we need
125                 // to report to, do so now.
126                 if(callback)
127                         if( callback->amount_complete(y,h) == false )
128                         {
129                                 // If the callback returns false,
130                                 // then the render has been aborted.
131
132                                 return false;
133                         }
134
135                 // Loop through every pixel in row
136                 for(x=0,u=su;x<w;x++,u+=du)
137                 {
138                         Color &c(*(colordata++));
139                         c=Color::alpha();
140
141                         // Loop through all subpixels
142                         for(y2=0,pool=0;y2<a;y2++)
143                                 for(x2=0;x2<a;x2++)
144                                 {
145                                         Color color=context.get_color(
146                                                 Point(
147                                                         u+(Point::value_type)(x2)*dsu,
148                                                         v+(Point::value_type)(y2)*dsv
149                                                         )
150                                                 );
151                                         if(!no_clamp)
152                                         {
153                                                 color=color.clamped();
154                                                 c+=color*color.get_a();
155                                                 pool+=color.get_a();
156                                         }
157                                         else
158                                         {
159                                                 c+=color*color.get_a();
160                                                 pool+=color.get_a();
161                                         }
162                                 }
163                         if(pool)
164                                 c/=pool;
165                 }
166         }
167
168         // Give the callback one more last call,
169         // this time with the full height as the
170         // current line
171         if(callback)
172                 callback->amount_complete(h,h);
173
174         // Report our success
175         return(true);
176 }
177
178 bool
179 synfig::render(
180         Context context,
181         Target_Scanline::Handle target,
182         const RendDesc &desc,
183         ProgressCallback *callback)
184 {
185         Point::value_type
186                 u,v,            // Current location in image
187                 su,sv,          // Starting locations
188                 du, dv,         // Distance between pixels
189                 dsu,dsv;        // Distance between subpixels
190
191         bool
192                 no_clamp=!desc.get_clamp();
193
194         int
195                 w(desc.get_w()),
196                 h(desc.get_h()),
197                 a(desc.get_antialias());
198
199         Point
200                 tl(desc.get_tl()),
201                 br(desc.get_br());
202
203         //Gamma
204         //      gamma(desc.get_gamma());
205
206         int
207                 x,y,            // Current location on output bitmap
208                 x2,y2;          // Subpixel counters
209
210         Color::value_type
211                 pool;           // Alpha pool (for correct alpha antialiasing)
212
213         assert(target);
214
215         // If we do not have a a target then bail
216         if(!target)
217                 return false;
218
219         // Calculate the number of channels
220         //chan=channels(desc.get_pixel_format());
221
222         // Calculate the distance between pixels
223         du=(br[0]-tl[0])/(Point::value_type)w;
224         dv=(br[1]-tl[1])/(Point::value_type)h;
225
226         // Calculate the distance between sub pixels
227         dsu=du/(Point::value_type)a;
228         dsv=dv/(Point::value_type)a;
229
230         // Calculate the starting points
231         su=tl[0]+(du-dsu)/(Point::value_type)2.0;
232         sv=tl[1]-(dv-dsv)/(Point::value_type)2.0;
233
234         // Mark the start of a new frame.
235         if(!target->start_frame(callback))
236                 return false;
237
238         // Loop through all horizontal lines
239         for(y=0,v=sv;y<h;y++,v+=dv)
240         {
241                 // Set the current pixel pointer
242                 // to the start of the line
243                 Color *colordata=target->start_scanline(y);
244
245                 if(!colordata)
246                 {
247                         if(callback)callback->error(_("Target panic"));
248                         else throw(string(_("Target panic")));
249                         return false;
250                 }
251
252                 // If we have a callback that we need
253                 // to report to, do so now.
254                 if(callback)
255                         if( callback->amount_complete(y,h) == false )
256                         {
257                                 // If the callback returns false,
258                                 // then the render has been aborted.
259                                 // Exit gracefuly.
260
261                                 target->end_scanline();
262                                 target->end_frame();
263                                 return false;
264                         }
265
266                 // Loop through every pixel in row
267                 for(x=0,u=su;x<w;x++,u+=du)
268                 {
269                         Color &c(*(colordata++));
270                         c=Color::alpha();
271
272                         // Loop through all subpixels
273                         for(y2=0,pool=0;y2<a;y2++)
274                                 for(x2=0;x2<a;x2++)
275                                 {
276                                         Color color=context.get_color(
277                                                 Point(
278                                                         u+(Point::value_type)(x2)*dsu,
279                                                         v+(Point::value_type)(y2)*dsv
280                                                         )
281                                                 );
282                                         if(!no_clamp)
283                                         {
284                                                 color=color.clamped();
285                                                 c+=color*color.get_a();
286                                                 pool+=color.get_a();
287                                         }
288                                         else
289                                         {
290                                                 c+=color*color.get_a();
291                                                 pool+=color.get_a();
292                                         }
293                                 }
294                         if(pool)
295                                 c/=pool;
296                 }
297
298                 // Send the buffer to the render target.
299                 // If anything goes wrong, cleanup and bail.
300                 if(!target->end_scanline())
301                 {
302                         if(callback)callback->error(_("Target panic"));
303                         else throw(string(_("Target panic")));
304                         return false;
305                 }
306         }
307
308         // Finish up the target's frame
309         target->end_frame();
310
311         // Give the callback one more last call,
312         // this time with the full height as the
313         // current line
314         if(callback)
315                 callback->amount_complete(h,h);
316
317         // Report our success
318         return(true);
319 }
320
321 bool
322 synfig::render_threaded(
323         Context context,
324         Target_Scanline::Handle target,
325         const RendDesc &desc,
326         ProgressCallback *callback,
327         int threads)
328 {
329 #ifndef WIN32
330     struct _render_thread
331     {
332                 int
333                         pipe_read,
334                         pipe_write,
335                         pid;
336                 _render_thread()
337                 {
338                         pipe(&pipe_read);
339                         pid=0;
340                 }
341                 ~_render_thread()
342                 {
343                         close(pipe_read);
344                         close(pipe_write);
345                         if(pid)
346                         {
347                                 kill(pid,9);
348                         }
349                 }
350     } *render_thread;
351
352     int i, mythread=-1;
353
354         Point::value_type
355                 u,v,            // Current location in image
356                 su,sv,          // Starting locations
357                 du, dv,         // Distance between pixels
358                 dsu,dsv;        // Distance between subpixels
359
360         bool
361                 no_clamp=!desc.get_clamp();
362
363         int
364                 w(desc.get_w()),
365                 h(desc.get_h()),
366                 a(desc.get_antialias());
367
368         Point
369                 tl(desc.get_tl()),
370                 br(desc.get_br());
371
372         int
373                 x,y,            // Current location on output bitmap
374                 x2,y2;          // Subpixel counters
375
376         Color::value_type
377                 pool;           // Alpha pool (for correct alpha antialiasing)
378
379         assert(target);
380
381         // If we do not have a a target then bail
382         if(!target)
383                 return false;
384
385         // Calculate the distance between pixels
386         du=(br[0]-tl[0])/(Point::value_type)w;
387         dv=(br[1]-tl[1])/(Point::value_type)h;
388
389         // Calculate the distance between sub pixels
390         dsu=du/(Point::value_type)a;
391         dsv=dv/(Point::value_type)a;
392
393         // Calculate the starting points
394         su=tl[0]+(du-dsu)/(Point::value_type)2.0;
395         sv=tl[1]-(dv-dsv)/(Point::value_type)2.0;
396
397     render_thread=new _render_thread[threads];
398
399         // Start the forks
400     for(i=0;i<threads;i++)
401     {
402                 int pid=fork();
403                 if(pid==0)
404                 {
405                 mythread=i;
406                 goto renderthread;
407                 }
408                 render_thread[i].pid=pid;
409     }
410
411         // Mark the start of a new frame.
412         if(!target->start_frame(callback))
413                 return false;
414
415     for(y=0;y<h;y++)
416     {
417                 // Set the current pixel pointer
418                 // to the start of the line
419                 Color *colordata(target->start_scanline(y));
420
421                 if(!colordata)
422                 {
423                         if(callback)callback->error(_("Target panic"));
424                         else throw(string(_("Target panic")));
425                         return false;
426                 }
427
428                 // If we have a callback that we need
429                 // to report to, do so now.
430                 if(callback)
431                         if( callback->amount_complete(y,h) == false )
432                         {
433                                 // If the callback returns false,
434                                 // then the render has been aborted.
435                                 // Exit gracefuly.
436
437                                 target->end_scanline();
438                                 target->end_frame();
439                                 delete [] render_thread;
440                                 return false;
441                         }
442
443                 read(render_thread[y%threads].pipe_read,colordata,w*sizeof(Color));
444
445                 // Send the buffer to the render target.
446                 // If anything goes wrong, cleanup and bail.
447                 if(!target->end_scanline())
448                 {
449                         delete [] render_thread;
450                         if(callback)callback->error(_("Target panic"));
451                         else throw(string(_("Target panic")));
452                         return false;
453                 }
454     }
455
456         // Finish up the target's frame
457         target->end_frame();
458
459         // Give the callback one more last call,
460         // this time with the full height as the
461         // current line
462         if(callback)
463                 callback->amount_complete(h,h);
464
465     delete [] render_thread;
466     return true;
467
468 renderthread:
469
470         // Change the random seed, so that each thread has a different one
471         srand(mythread*20+threads+time(0));
472
473         Color *buffer(new Color[w]);
474
475         // Loop through all horizontal lines
476         for(y=mythread,v=sv+dv*(Real)mythread;y<h;y+=threads,v+=dv*(Real)threads)
477         {
478                 // Set the current pixel pointer
479                 // to the start of the line
480                 Color* colordata(buffer);
481
482                 // Loop through every pixel in row
483                 for(x=0,u=su;x<w;x++,u+=du)
484                 {
485                         Color &c(*(colordata++));
486                         c=Color::alpha();
487
488                         // Loop through all subpixels
489                         for(y2=0,pool=0;y2<a;y2++)
490                                 for(x2=0;x2<a;x2++)
491                                 {
492                                         Color color=context.get_color(
493                                                 Point(
494                                                         u+(Point::value_type)(x2)*dsu,
495                                                         v+(Point::value_type)(y2)*dsv
496                                                         )
497                                                 );
498                                         if(!no_clamp)
499                                         {
500                                                 color=color.clamped();
501                                                 c+=color*color.get_a();
502                                                 pool+=color.get_a();
503                                         }
504                                         else
505                                         {
506                                                 c+=color*color.get_a();
507                                                 pool+=color.get_a();
508                                         }
509                                 }
510                         if(pool)
511                                 c/=pool;
512                 }
513
514                 // Send the buffer to the primary thread.
515                 write(render_thread[mythread].pipe_write,buffer,w*sizeof(Color));
516         }
517
518         delete [] buffer;
519
520     _exit(0);
521         return false;
522 #else
523         return render(context, target, desc, callback);
524
525 #endif
526 }