Enable $Id$ expansion.
[synfig.git] / synfig-core / trunk / src / modules / lyr_std / warp.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file warp.cpp
3 **      \brief Template File
4 **
5 **      \legal
6 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
7 **
8 **      This package is free software; you can redistribute it and/or
9 **      modify it under the terms of the GNU General Public License as
10 **      published by the Free Software Foundation; either version 2 of
11 **      the License, or (at your option) any later version.
12 **
13 **      This package is distributed in the hope that it will be useful,
14 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
15 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 **      General Public License for more details.
17 **      \endlegal
18 **
19 ** === N O T E S ===========================================================
20 **
21 ** ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "warp.h"
33 #include <synfig/string.h>
34 #include <synfig/time.h>
35 #include <synfig/context.h>
36 #include <synfig/paramdesc.h>
37 #include <synfig/renddesc.h>
38 #include <synfig/surface.h>
39 #include <synfig/value.h>
40 #include <synfig/valuenode.h>
41 #include <synfig/transform.h>
42 #include <ETL/misc>
43
44 #endif
45
46 /* === M A C R O S ========================================================= */
47
48 /* === G L O B A L S ======================================================= */
49
50 SYNFIG_LAYER_INIT(Warp);
51 SYNFIG_LAYER_SET_NAME(Warp,"warp");
52 SYNFIG_LAYER_SET_LOCAL_NAME(Warp,_("Warp"));
53 SYNFIG_LAYER_SET_CATEGORY(Warp,_("Distortions"));
54 SYNFIG_LAYER_SET_VERSION(Warp,"0.1");
55 SYNFIG_LAYER_SET_CVS_ID(Warp,"$Id$");
56
57 /* === P R O C E D U R E S ================================================= */
58
59 /* === M E T H O D S ======================================================= */
60
61 /* === E N T R Y P O I N T ================================================= */
62
63 Warp::Warp():
64         src_tl  (-2,2),
65         src_br  (2,-2),
66         dest_tl (-1.8,2.1),
67         dest_tr (1.8,2.1),
68         dest_br (2.2,-2),
69         dest_bl (-2.2,-2),
70         clip    (true)
71 {
72         sync();
73         horizon=4;
74 }
75
76 Warp::~Warp()
77 {
78 }
79
80 inline Point
81 Warp::transform_forward(const Point& p)const
82 {
83         return Point(
84                 (inv_matrix[0][0]*p[0] + inv_matrix[0][1]*p[1] + inv_matrix[0][2])/(inv_matrix[2][0]*p[0] + inv_matrix[2][1]*p[1] + inv_matrix[2][2]),
85                 (inv_matrix[1][0]*p[0] + inv_matrix[1][1]*p[1] + inv_matrix[1][2])/(inv_matrix[2][0]*p[0] + inv_matrix[2][1]*p[1] + inv_matrix[2][2])
86         );
87 }
88
89 inline Point
90 Warp::transform_backward(const Point& p)const
91 {
92         return Point(
93                 (matrix[0][0]*p[0] + matrix[0][1]*p[1] + matrix[0][2])/(matrix[2][0]*p[0] + matrix[2][1]*p[1] + matrix[2][2]),
94                 (matrix[1][0]*p[0] + matrix[1][1]*p[1] + matrix[1][2])/(matrix[2][0]*p[0] + matrix[2][1]*p[1] + matrix[2][2])
95         );
96 }
97
98 inline Real
99 Warp::transform_forward_z(const Point& p)const
100 {
101         return inv_matrix[2][0]*p[0] + inv_matrix[2][1]*p[1] + inv_matrix[2][2];
102 }
103
104 inline Real
105 Warp::transform_backward_z(const Point& p)const
106 {
107         return matrix[2][0]*p[0] + matrix[2][1]*p[1] + matrix[2][2];
108 }
109
110 /*
111 #define transform_forward(p) Point(     \
112                 cache_a*p[0] + cache_b*p[1] + cache_c*p[0]*p[1] + cache_d,      \
113                 cache_e*p[0] + cache_f*p[1] + cache_i*p[0]*p[1] + cache_j )
114
115 #define transform_backward(p) Point(    \
116                 cache_a*p[0] + cache_b*p[1] + cache_c*p[0]*p[1] + cache_d,      \
117                 cache_e*p[0] + cache_f*p[1] + cache_i*p[0]*p[1] + cache_j )
118 */
119
120 #define triangle_area(a,b,c)    (0.5*(-b[0]*a[1]+c[0]*a[1]+a[0]*b[1]-c[0]*b[1]-a[0]*c[1]+b[0]*c[1]))
121 #define quad_area(a,b,c,d) (triangle_area(a,b,c)+triangle_area(a,c,d))
122
123 Real mat3_determinant(Real matrix[3][3])
124 {
125   Real ret;
126
127   ret  = (matrix[0][0] *
128                   (matrix[1][1] * matrix[2][2] -
129                    matrix[1][2] * matrix[2][1]));
130   ret -= (matrix[1][0] *
131                   (matrix[0][1] * matrix[2][2] -
132                    matrix[0][2] * matrix[2][1]));
133   ret += (matrix[2][0] *
134                   (matrix[0][1] * matrix[1][2] -
135                    matrix[0][2] * matrix[1][1]));
136
137 return ret;
138 }
139
140 void mat3_invert(Real in[3][3], Real out[3][3])
141 {
142   Real det(mat3_determinant(in));
143
144         if (det == 0.0)
145     return;
146
147   det = 1.0 / det;
148
149   out[0][0] =   (in[1][1] * in[2][2] -
150                        in[1][2] * in[2][1]) * det;
151
152   out[1][0] = - (in[1][0] * in[2][2] -
153                        in[1][2] * in[2][0]) * det;
154
155   out[2][0] =   (in[1][0] * in[2][1] -
156                        in[1][1] * in[2][0]) * det;
157
158   out[0][1] = - (in[0][1] * in[2][2] -
159                        in[0][2] * in[2][1]) * det;
160
161   out[1][1] =   (in[0][0] * in[2][2] -
162                        in[0][2] * in[2][0]) * det;
163
164   out[2][1] = - (in[0][0] * in[2][1] -
165                        in[0][1] * in[2][0]) * det;
166
167   out[0][2] =   (in[0][1] * in[1][2] -
168                        in[0][2] * in[1][1]) * det;
169
170   out[1][2] = - (in[0][0] * in[1][2] -
171                        in[0][2] * in[1][0]) * det;
172
173   out[2][2] =   (in[0][0] * in[1][1] -
174                        in[0][1] * in[1][0]) * det;
175
176 }
177
178 void
179 Warp::sync()
180 {
181 /*      cache_a=(-dest_tl[0]+dest_tr[0])/(src_br[1]-src_tl[1]);
182         cache_b=(-dest_tl[0]+dest_bl[0])/(src_br[0]-src_tl[0]);
183         cache_c=(dest_tl[0]-dest_tr[0]+dest_br[0]-dest_bl[0])/((src_br[1]-src_tl[1])*(src_br[0]-src_tl[0]));
184         cache_d=dest_tl[0];
185
186         cache_e=(-dest_tl[1]+dest_tr[1])/(src_br[0]-src_tl[0]);
187         cache_f=(-dest_tl[1]+dest_bl[1])/(src_br[1]-src_tl[1]);
188         cache_i=(dest_tl[1]-dest_tr[1]+dest_br[1]-dest_bl[1])/((src_br[1]-src_tl[1])*(src_br[0]-src_tl[0]));
189         cache_j=dest_tl[1];
190 */
191
192 /*      matrix[2][0]=(dest_tl[0]-dest_tr[0]+dest_br[0]-dest_bl[0])/((src_br[1]-src_tl[1])*(src_br[0]-src_tl[0]));
193         matrix[2][1]=(dest_tl[1]-dest_tr[1]+dest_br[1]-dest_bl[1])/((src_br[1]-src_tl[1])*(src_br[0]-src_tl[0]));
194         matrix[2][2]=quad_area(dest_tl,dest_tr,dest_br,dest_bl)/((src_br[1]-src_tl[1])*(src_br[0]-src_tl[0]));
195
196         matrix[0][0]=-(-dest_tl[1]+dest_tr[1])/(src_br[0]-src_tl[0]);
197         matrix[0][1]=-(-dest_tl[1]+dest_bl[1])/(src_br[1]-src_tl[1]);
198
199         matrix[1][0]=-(-dest_tl[0]+dest_tr[0])/(src_br[1]-src_tl[1]);
200         matrix[1][1]=-(-dest_tl[0]+dest_bl[0])/(src_br[0]-src_tl[0]);
201
202         matrix[0][2]=matrix[0][0]*dest_tl[0] + matrix[0][1]*dest_tl[1];
203         matrix[1][2]=matrix[1][0]*dest_tl[0] + matrix[1][1]*dest_tl[1];
204 */
205
206 #define matrix tmp
207         Real tmp[3][3];
208
209         const Real& x1(min(src_br[0],src_tl[0]));
210         const Real& y1(min(src_br[1],src_tl[1]));
211         const Real& x2(max(src_br[0],src_tl[0]));
212         const Real& y2(max(src_br[1],src_tl[1]));
213
214         Real tx1(dest_bl[0]);
215         Real ty1(dest_bl[1]);
216         Real tx2(dest_br[0]);
217         Real ty2(dest_br[1]);
218         Real tx3(dest_tl[0]);
219         Real ty3(dest_tl[1]);
220         Real tx4(dest_tr[0]);
221         Real ty4(dest_tr[1]);
222
223         if(src_br[0]<src_tl[0])
224                 swap(tx3,tx4),swap(ty3,ty4),swap(tx1,tx2),swap(ty1,ty2);
225
226         if(src_br[1]>src_tl[1])
227                 swap(tx3,tx1),swap(ty3,ty1),swap(tx4,tx2),swap(ty4,ty2);
228
229         Real scalex;
230         Real scaley;
231
232   scalex = scaley = 1.0;
233
234   if ((x2 - x1) > 0)
235     scalex = 1.0 / (Real) (x2 - x1);
236
237   if ((y2 - y1) > 0)
238     scaley = 1.0 / (Real) (y2 - y1);
239
240   /* Determine the perspective transform that maps from
241    * the unit cube to the transformed coordinates
242    */
243   {
244     Real dx1, dx2, dx3, dy1, dy2, dy3;
245
246     dx1 = tx2 - tx4;
247     dx2 = tx3 - tx4;
248     dx3 = tx1 - tx2 + tx4 - tx3;
249
250     dy1 = ty2 - ty4;
251     dy2 = ty3 - ty4;
252     dy3 = ty1 - ty2 + ty4 - ty3;
253
254     /*  Is the mapping affine?  */
255     if ((dx3 == 0.0) && (dy3 == 0.0))
256       {
257         matrix[0][0] = tx2 - tx1;
258         matrix[0][1] = tx4 - tx2;
259         matrix[0][2] = tx1;
260         matrix[1][0] = ty2 - ty1;
261         matrix[1][1] = ty4 - ty2;
262         matrix[1][2] = ty1;
263         matrix[2][0] = 0.0;
264         matrix[2][1] = 0.0;
265       }
266     else
267       {
268         Real det1, det2;
269
270         det1 = dx3 * dy2 - dy3 * dx2;
271         det2 = dx1 * dy2 - dy1 * dx2;
272
273         if (det1 == 0.0 && det2 == 0.0)
274           matrix[2][0] = 1.0;
275         else
276           matrix[2][0] = det1 / det2;
277
278         det1 = dx1 * dy3 - dy1 * dx3;
279
280         if (det1 == 0.0 && det2 == 0.0)
281           matrix[2][1] = 1.0;
282         else
283           matrix[2][1] = det1 / det2;
284
285         matrix[0][0] = tx2 - tx1 + matrix[2][0] * tx2;
286         matrix[0][1] = tx3 - tx1 + matrix[2][1] * tx3;
287         matrix[0][2] = tx1;
288
289         matrix[1][0] = ty2 - ty1 + matrix[2][0] * ty2;
290         matrix[1][1] = ty3 - ty1 + matrix[2][1] * ty3;
291         matrix[1][2] = ty1;
292       }
293
294     matrix[2][2] = 1.0;
295   }
296 #undef matrix
297
298         Real scaletrans[3][3]={
299                         { scalex, 0, -x1*scalex },
300                         { 0, scaley, -y1*scaley },
301                         { 0, 0, 1 }
302         };
303
304         Real t1,t2,t3;
305
306         for (int i = 0; i < 3; i++)
307     {
308       t1 = tmp[i][0];
309       t2 = tmp[i][1];
310       t3 = tmp[i][2];
311
312       for (int j = 0; j < 3; j++)
313         {
314           matrix[i][j]  = t1 * scaletrans[0][j];
315           matrix[i][j] += t2 * scaletrans[1][j];
316           matrix[i][j] += t3 * scaletrans[2][j];
317         }
318     }
319
320         mat3_invert(matrix, inv_matrix);
321 /*
322         gimp_matrix3_identity  (result);
323   gimp_matrix3_translate (result, -x1, -y1);
324   gimp_matrix3_scale     (result, scalex, scaley);
325   gimp_matrix3_mult      (&matrix, result);
326 */
327 }
328
329 bool
330 Warp::set_param(const String & param, const ValueBase &value)
331 {
332         IMPORT_PLUS(src_tl,sync());
333         IMPORT_PLUS(src_br,sync());
334         IMPORT_PLUS(dest_tl,sync());
335         IMPORT_PLUS(dest_tr,sync());
336         IMPORT_PLUS(dest_bl,sync());
337         IMPORT_PLUS(dest_br,sync());
338         IMPORT(clip);
339         IMPORT(horizon);
340
341         return false;
342 }
343
344 ValueBase
345 Warp::get_param(const String &param)const
346 {
347         EXPORT(src_tl);
348         EXPORT(src_br);
349         EXPORT(dest_tl);
350         EXPORT(dest_tr);
351         EXPORT(dest_bl);
352         EXPORT(dest_br);
353         EXPORT(clip);
354         EXPORT(horizon);
355
356         EXPORT_NAME();
357         EXPORT_VERSION();
358
359         return ValueBase();
360 }
361
362 Layer::Vocab
363 Warp::get_param_vocab()const
364 {
365         Layer::Vocab ret;
366
367         ret.push_back(ParamDesc("src_tl")
368                 .set_local_name(_("Source TL"))
369                 .set_box("src_br")
370         );
371
372         ret.push_back(ParamDesc("src_br")
373                 .set_local_name(_("Source BR"))
374         );
375
376         ret.push_back(ParamDesc("dest_tl")
377                 .set_local_name(_("Dest TL"))
378                 .set_connect("dest_tr")
379         );
380
381         ret.push_back(ParamDesc("dest_tr")
382                 .set_local_name(_("Dest TR"))
383                 .set_connect("dest_br")
384         );
385
386         ret.push_back(ParamDesc("dest_br")
387                 .set_local_name(_("Dest BR"))
388                 .set_connect("dest_bl")
389         );
390
391         ret.push_back(ParamDesc("dest_bl")
392                 .set_local_name(_("Dest BL"))
393                 .set_connect("dest_tl")
394         );
395
396         ret.push_back(ParamDesc("clip")
397                 .set_local_name(_("Clip"))
398         );
399
400         ret.push_back(ParamDesc("horizon")
401                 .set_local_name(_("Horizon"))
402         );
403
404         return ret;
405 }
406
407
408 class Warp_Trans : public Transform
409 {
410         etl::handle<const Warp> layer;
411 public:
412         Warp_Trans(const Warp* x):Transform(x->get_guid()),layer(x) { }
413
414         synfig::Vector perform(const synfig::Vector& x)const
415         {
416                 return layer->transform_backward(x);
417                 //Point pos(x-layer->origin);
418                 //return Point(layer->cos_val*pos[0]-layer->sin_val*pos[1],layer->sin_val*pos[0]+layer->cos_val*pos[1])+layer->origin;
419         }
420
421         synfig::Vector unperform(const synfig::Vector& x)const
422         {
423
424                 return layer->transform_forward(x);
425                 //Point pos(x-layer->origin);
426                 //return Point(layer->cos_val*pos[0]+layer->sin_val*pos[1],-layer->sin_val*pos[0]+layer->cos_val*pos[1])+layer->origin;
427         }
428 };
429 etl::handle<Transform>
430 Warp::get_transform()const
431 {
432         return new Warp_Trans(this);
433 }
434
435 synfig::Layer::Handle
436 Warp::hit_check(synfig::Context context, const synfig::Point &p)const
437 {
438         Point newpos(transform_forward(p));
439
440         if(clip)
441         {
442                 Rect rect(src_tl,src_br);
443                 if(!rect.is_inside(newpos))
444                         return 0;
445         }
446
447         return context.hit_check(newpos);
448 }
449
450 Color
451 Warp::get_color(Context context, const Point &p)const
452 {
453         Point newpos(transform_forward(p));
454
455         if(clip)
456         {
457                 Rect rect(src_tl,src_br);
458                 if(!rect.is_inside(newpos))
459                         return Color::alpha();
460         }
461
462         const float z(transform_backward_z(newpos));
463         if(z>0 && z<horizon)
464                 return context.get_color(newpos);
465         else
466                 return Color::alpha();
467 }
468
469 //#define ACCEL_WARP_IS_BROKEN 1
470
471 bool
472 Warp::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
473 {
474         SuperCallback stageone(cb,0,9000,10000);
475         SuperCallback stagetwo(cb,9000,10000,10000);
476
477         Real pw=(renddesc.get_w())/(renddesc.get_br()[0]-renddesc.get_tl()[0]);
478         Real ph=(renddesc.get_h())/(renddesc.get_br()[1]-renddesc.get_tl()[1]);
479
480         if(cb && !cb->amount_complete(0,10000))
481                 return false;
482
483         Point tl(renddesc.get_tl());
484         Point br(renddesc.get_br());
485
486         Rect bounding_rect;
487
488         Rect render_rect(tl,br);
489         Rect clip_rect(Rect::full_plane());
490         Rect dest_rect(dest_tl,dest_br); dest_rect.expand(dest_tr).expand(dest_bl);
491
492         Real zoom_factor(1.0);
493
494         // Quick exclusion clip, if necessary
495         if(clip && !intersect(render_rect,dest_rect))
496         {
497                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
498                 surface->clear();
499                 return true;
500         }
501
502         {
503                 Rect other(render_rect);
504                 if(clip)
505                         other&=dest_rect;
506
507                 Point min(other.get_min());
508                 Point max(other.get_max());
509
510                 bool init_point_set=false;
511
512                 // Point trans_point[4];
513                 Point p;
514                 // Real trans_z[4];
515                 Real z,minz(10000000000000.0f),maxz(0);
516
517
518                 p=transform_forward(min);
519                 z=transform_backward_z(p);
520                 if(z>0 && z<horizon*2)
521                 {
522                         if(init_point_set)
523                                 bounding_rect.expand(p);
524                         else
525                                 bounding_rect=Rect(p);
526                         init_point_set=true;
527                         maxz=std::max(maxz,z);
528                         minz=std::min(minz,z);
529                 }
530
531                 p=transform_forward(max);
532                 z=transform_backward_z(p);
533                 if(z>0 && z<horizon*2)
534                 {
535                         if(init_point_set)
536                                 bounding_rect.expand(p);
537                         else
538                                 bounding_rect=Rect(p);
539                         init_point_set=true;
540                         maxz=std::max(maxz,z);
541                         minz=std::min(minz,z);
542                 }
543
544                 swap(min[1],max[1]);
545
546                 p=transform_forward(min);
547                 z=transform_backward_z(p);
548                 if(z>0 && z<horizon*2)
549                 {
550                         if(init_point_set)
551                                 bounding_rect.expand(p);
552                         else
553                                 bounding_rect=Rect(p);
554                         init_point_set=true;
555                         maxz=std::max(maxz,z);
556                         minz=std::min(minz,z);
557                 }
558
559                 p=transform_forward(max);
560                 z=transform_backward_z(p);
561                 if(z>0 && z<horizon*2)
562                 {
563                         if(init_point_set)
564                                 bounding_rect.expand(p);
565                         else
566                                 bounding_rect=Rect(p);
567                         init_point_set=true;
568                         maxz=std::max(maxz,z);
569                         minz=std::min(minz,z);
570                 }
571
572                 if(!init_point_set)
573                 {
574                         surface->set_wh(renddesc.get_w(),renddesc.get_h());
575                         surface->clear();
576                         return true;
577                 }
578                 zoom_factor=(1+(maxz-minz));
579
580         }
581
582 #ifdef ACCEL_WARP_IS_BROKEN
583         return Layer::accelerated_render(context,surface,quality,renddesc, cb);
584 #else
585
586         /*swap(tl[1],br[1]);
587         bounding_rect
588                 .expand(transform_forward(tl))
589                 .expand(transform_forward(br))
590         ;
591         swap(tl[1],br[1]);*/
592
593         //synfig::warning("given window: [%f,%f]-[%f,%f] %dx%d",tl[0],tl[1],br[0],br[1],renddesc.get_w(),renddesc.get_h());
594         //synfig::warning("Projected: [%f,%f]-[%f,%f]",bounding_rect.get_min()[0],bounding_rect.get_min()[1],bounding_rect.get_max()[0],bounding_rect.get_max()[1]);
595
596         // If we are clipping, then go ahead and clip to the
597         // source rectangle
598         if(clip)
599                 clip_rect&=Rect(src_tl,src_br);
600
601         // Bound ourselves to the bounding rectangle of
602         // what is under us
603         clip_rect&=context.get_full_bounding_rect();//.expand_x(abs(zoom_factor/pw)).expand_y(abs(zoom_factor/ph));
604
605         bounding_rect&=clip_rect;
606
607         Point min_point(bounding_rect.get_min());
608         Point max_point(bounding_rect.get_max());
609
610
611         if(tl[0]>br[0])
612         {
613                 tl[0]=max_point[0];
614                 br[0]=min_point[0];
615         }
616         else
617         {
618                 br[0]=max_point[0];
619                 tl[0]=min_point[0];
620         }
621         if(tl[1]>br[1])
622         {
623                 tl[1]=max_point[1];
624                 br[1]=min_point[1];
625         }
626         else
627         {
628                 br[1]=max_point[1];
629                 tl[1]=min_point[1];
630         }
631
632
633
634         const int tmp_d(max(renddesc.get_w(),renddesc.get_h()));
635         Real src_pw=(tmp_d*zoom_factor)/(br[0]-tl[0]);
636         Real src_ph=(tmp_d*zoom_factor)/(br[1]-tl[1]);
637
638
639         RendDesc desc(renddesc);
640         desc.clear_flags();
641         //desc.set_flags(RendDesc::PX_ASPECT);
642         desc.set_tl(tl);
643         desc.set_br(br);
644         desc.set_wh(ceil_to_int(src_pw*(br[0]-tl[0])),ceil_to_int(src_ph*(br[1]-tl[1])));
645
646         //synfig::warning("surface to render: [%f,%f]-[%f,%f] %dx%d",desc.get_tl()[0],desc.get_tl()[1],desc.get_br()[0],desc.get_br()[1],desc.get_w(),desc.get_h());
647         if(desc.get_w()==0 && desc.get_h()==0)
648         {
649                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
650                 surface->clear();
651                 return true;
652         }
653
654         // Recalculate the pixel widths for the src renddesc
655         src_pw=(desc.get_w())/(desc.get_br()[0]-desc.get_tl()[0]);
656         src_ph=(desc.get_h())/(desc.get_br()[1]-desc.get_tl()[1]);
657
658
659         Surface source;
660         source.set_wh(desc.get_w(),desc.get_h());
661
662         if(!context.accelerated_render(&source,quality,desc,&stageone))
663                 return false;
664
665         surface->set_wh(renddesc.get_w(),renddesc.get_h());
666         surface->clear();
667
668         Surface::pen pen(surface->begin());
669
670         if(quality<=4)
671         {
672                 // CUBIC
673                 int x,y;
674                 float u,v;
675                 Point point,tmp;
676                 for(y=0,point[1]=renddesc.get_tl()[1];y<surface->get_h();y++,pen.inc_y(),pen.dec_x(x),point[1]+=1.0/ph)
677                 {
678                         for(x=0,point[0]=renddesc.get_tl()[0];x<surface->get_w();x++,pen.inc_x(),point[0]+=1.0/pw)
679                         {
680                                 tmp=transform_forward(point);
681                                 const float z(transform_backward_z(tmp));
682                                 if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
683                                 {
684                                         (*surface)[y][x]=Color::alpha();
685                                         continue;
686                                 }
687
688                                 u=(tmp[0]-tl[0])*src_pw;
689                                 v=(tmp[1]-tl[1])*src_ph;
690
691                                 if(u<0 || v<0 || u>=source.get_w() || v>=source.get_h() || isnan(u) || isnan(v))
692                                 {
693                                         (*surface)[y][x]=context.get_color(tmp);
694                                 }
695                                 else
696                                         (*surface)[y][x]=source.cubic_sample(u,v);
697                         }
698                         if(y&31==0 && cb)
699                         {
700                                 if(!stagetwo.amount_complete(y,surface->get_h()))
701                                         return false;
702                         }
703                 }
704         }
705         else
706         if(quality<=6)
707         {
708                 // INTERPOLATION_LINEAR
709                 int x,y;
710                 float u,v;
711                 Point point,tmp;
712                 for(y=0,point[1]=renddesc.get_tl()[1];y<surface->get_h();y++,pen.inc_y(),pen.dec_x(x),point[1]+=1.0/ph)
713                 {
714                         for(x=0,point[0]=renddesc.get_tl()[0];x<surface->get_w();x++,pen.inc_x(),point[0]+=1.0/pw)
715                         {
716                                 tmp=transform_forward(point);
717                                 const float z(transform_backward_z(tmp));
718                                 if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
719                                 {
720                                         (*surface)[y][x]=Color::alpha();
721                                         continue;
722                                 }
723
724                                 u=(tmp[0]-tl[0])*src_pw;
725                                 v=(tmp[1]-tl[1])*src_ph;
726
727                                 if(u<0 || v<0 || u>=source.get_w() || v>=source.get_h() || isnan(u) || isnan(v))
728                                 {
729                                         if(clip)
730                                                 (*surface)[y][x]=Color::alpha();
731                                         else
732                                                 (*surface)[y][x]=context.get_color(tmp);
733                                 }
734                                 else
735                                         (*surface)[y][x]=source.linear_sample(u,v);
736                         }
737                         if(y&31==0 && cb)
738                         {
739                                 if(!stagetwo.amount_complete(y,surface->get_h()))
740                                         return false;
741                         }
742                 }
743         }
744         else
745         {
746                 // NEAREST_NEIGHBOR
747                 int x,y;
748                 float u,v;
749                 Point point,tmp;
750                 for(y=0,point[1]=renddesc.get_tl()[1];y<surface->get_h();y++,pen.inc_y(),pen.dec_x(x),point[1]+=1.0/ph)
751                 {
752                         for(x=0,point[0]=renddesc.get_tl()[0];x<surface->get_w();x++,pen.inc_x(),point[0]+=1.0/pw)
753                         {
754                                 tmp=transform_forward(point);
755                                 const float z(transform_backward_z(tmp));
756                                 if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
757                                 {
758                                         (*surface)[y][x]=Color::alpha();
759                                         continue;
760                                 }
761
762                                 u=(tmp[0]-tl[0])*src_pw;
763                                 v=(tmp[1]-tl[1])*src_ph;
764
765                                 if(u<0 || v<0 || u>=source.get_w() || v>=source.get_h() || isnan(u) || isnan(v))
766                                 {
767                                         if(clip)
768                                                 (*surface)[y][x]=Color::alpha();
769                                         else
770                                                 (*surface)[y][x]=context.get_color(tmp);
771                                 }
772                                 else
773                                 //pen.set_value(source[v][u]);
774                                 (*surface)[y][x]=source[floor_to_int(v)][floor_to_int(u)];
775                         }
776                         if(y&31==0 && cb)
777                         {
778                                 if(!stagetwo.amount_complete(y,surface->get_h()))
779                                         return false;
780                         }
781                 }
782         }
783
784 #endif
785
786         if(cb && !cb->amount_complete(10000,10000)) return false;
787
788         return true;
789 }
790
791 synfig::Rect
792 Warp::get_bounding_rect()const
793 {
794         return Rect::full_plane();
795 }
796
797 synfig::Rect
798 Warp::get_full_bounding_rect(Context context)const
799 {
800 //      return Rect::full_plane();
801
802         Rect under(context.get_full_bounding_rect());
803
804         if(clip)
805         {
806                 under&=Rect(src_tl,src_br);
807         }
808
809         return get_transform()->perform(under);
810
811         /*
812         Rect under(context.get_full_bounding_rect());
813         Rect ret(Rect::zero());
814
815         if(under.area()==HUGE_VAL)
816                 return Rect::full_plane();
817
818         ret.expand(
819                 transform_backward(
820                         under.get_min()
821                 )
822         );
823         ret.expand(
824                 transform_backward(
825                         under.get_max()
826                 )
827         );
828         ret.expand(
829                 transform_backward(
830                         Vector(
831                                 under.get_min()[0],
832                                 under.get_max()[1]
833                         )
834                 )
835         );
836         ret.expand(
837                 transform_backward(
838                         Vector(
839                                 under.get_max()[0],
840                                 under.get_min()[1]
841                         )
842                 )
843         );
844
845         if(ret.area()==HUGE_VAL)
846                 return Rect::full_plane();
847
848         return ret;
849         */
850 }