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