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