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