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