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 **	Copyright (c) 2011-2013 Carlos López
11 **
12 **	This package is free software; you can redistribute it and/or
13 **	modify it under the terms of the GNU General Public License as
14 **	published by the Free Software Foundation; either version 2 of
15 **	the License, or (at your option) any later version.
16 **
17 **	This package is distributed in the hope that it will be useful,
18 **	but WITHOUT ANY WARRANTY; without even the implied warranty of
19 **	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 **	General Public License for more details.
21 **	\endlegal
22 **
23 ** === N O T E S ===========================================================
24 **
25 ** ========================================================================= */
26 
27 /* === H E A D E R S ======================================================= */
28 
29 #ifdef USING_PCH
30 #	include "pch.h"
31 #else
32 #ifdef HAVE_CONFIG_H
33 #	include <config.h>
34 #endif
35 
36 #include "warp.h"
37 
38 #include <synfig/localization.h>
39 #include <synfig/general.h>
40 
41 #include <synfig/string.h>
42 #include <synfig/time.h>
43 #include <synfig/context.h>
44 #include <synfig/paramdesc.h>
45 #include <synfig/renddesc.h>
46 #include <synfig/surface.h>
47 #include <synfig/value.h>
48 #include <synfig/valuenode.h>
49 #include <synfig/transform.h>
50 #include <synfig/cairo_renddesc.h>
51 #include <ETL/misc>
52 
53 #endif
54 
55 using namespace std;
56 using namespace etl;
57 using namespace synfig;
58 using namespace modules;
59 using namespace lyr_std;
60 
61 /* === M A C R O S ========================================================= */
62 
63 /* === G L O B A L S ======================================================= */
64 
65 SYNFIG_LAYER_INIT(Warp);
66 SYNFIG_LAYER_SET_NAME(Warp,"warp");
67 SYNFIG_LAYER_SET_LOCAL_NAME(Warp,N_("Warp"));
68 SYNFIG_LAYER_SET_CATEGORY(Warp,N_("Distortions"));
69 SYNFIG_LAYER_SET_VERSION(Warp,"0.1");
70 SYNFIG_LAYER_SET_CVS_ID(Warp,"$Id$");
71 
72 /* === P R O C E D U R E S ================================================= */
73 
74 /* === M E T H O D S ======================================================= */
75 
76 /* === E N T R Y P O I N T ================================================= */
77 
Warp()78 Warp::Warp():
79 	param_src_tl  (ValueBase(Point(-2,2))),
80 	param_src_br  (ValueBase(Point(2,-2))),
81 	param_dest_tl (ValueBase(Point(-1.8,2.1))),
82 	param_dest_tr (ValueBase(Point(1.8,2.1))),
83 	param_dest_bl (ValueBase(Point(-2.2,-2))),
84 	param_dest_br (ValueBase(Point(2.2,-2))),
85 	param_clip	  (ValueBase(true))
86 {
87 	param_horizon=ValueBase(Real(4));
88 	sync();
89 	SET_INTERPOLATION_DEFAULTS();
90 	SET_STATIC_DEFAULTS();
91 }
92 
~Warp()93 Warp::~Warp()
94 {
95 }
96 
97 inline Point
transform_forward(const Point & p) const98 Warp::transform_forward(const Point& p)const
99 {
100 	return Point(
101 		(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]),
102 		(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])
103 	);
104 }
105 
106 inline Point
transform_backward(const Point & p) const107 Warp::transform_backward(const Point& p)const
108 {
109 	return Point(
110 		(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]),
111 		(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])
112 	);
113 }
114 
115 inline Real
transform_forward_z(const Point & p) const116 Warp::transform_forward_z(const Point& p)const
117 {
118 	return inv_matrix[2][0]*p[0] + inv_matrix[2][1]*p[1] + inv_matrix[2][2];
119 }
120 
121 inline Real
transform_backward_z(const Point & p) const122 Warp::transform_backward_z(const Point& p)const
123 {
124 	return matrix[2][0]*p[0] + matrix[2][1]*p[1] + matrix[2][2];
125 }
126 
127 /*
128 #define transform_forward(p) Point(	\
129 		cache_a*p[0] + cache_b*p[1] + cache_c*p[0]*p[1] + cache_d,	\
130 		cache_e*p[0] + cache_f*p[1] + cache_i*p[0]*p[1] + cache_j )
131 
132 #define transform_backward(p) Point(	\
133 		cache_a*p[0] + cache_b*p[1] + cache_c*p[0]*p[1] + cache_d,	\
134 		cache_e*p[0] + cache_f*p[1] + cache_i*p[0]*p[1] + cache_j )
135 */
136 
137 #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]))
138 #define quad_area(a,b,c,d) (triangle_area(a,b,c)+triangle_area(a,c,d))
139 
mat3_determinant(Real matrix[3][3])140 Real mat3_determinant(Real matrix[3][3])
141 {
142   Real ret;
143 
144   ret  = (matrix[0][0] *
145                   (matrix[1][1] * matrix[2][2] -
146                    matrix[1][2] * matrix[2][1]));
147   ret -= (matrix[1][0] *
148                   (matrix[0][1] * matrix[2][2] -
149                    matrix[0][2] * matrix[2][1]));
150   ret += (matrix[2][0] *
151                   (matrix[0][1] * matrix[1][2] -
152                    matrix[0][2] * matrix[1][1]));
153 
154 return ret;
155 }
156 
mat3_invert(Real in[3][3],Real out[3][3])157 void mat3_invert(Real in[3][3], Real out[3][3])
158 {
159   Real det(mat3_determinant(in));
160 
161 	if (det == 0.0)
162     return;
163 
164   det = 1.0 / det;
165 
166   out[0][0] =   (in[1][1] * in[2][2] -
167                        in[1][2] * in[2][1]) * det;
168 
169   out[1][0] = - (in[1][0] * in[2][2] -
170                        in[1][2] * in[2][0]) * det;
171 
172   out[2][0] =   (in[1][0] * in[2][1] -
173                        in[1][1] * in[2][0]) * det;
174 
175   out[0][1] = - (in[0][1] * in[2][2] -
176                        in[0][2] * in[2][1]) * det;
177 
178   out[1][1] =   (in[0][0] * in[2][2] -
179                        in[0][2] * in[2][0]) * det;
180 
181   out[2][1] = - (in[0][0] * in[2][1] -
182                        in[0][1] * in[2][0]) * det;
183 
184   out[0][2] =   (in[0][1] * in[1][2] -
185                        in[0][2] * in[1][1]) * det;
186 
187   out[1][2] = - (in[0][0] * in[1][2] -
188                        in[0][2] * in[1][0]) * det;
189 
190   out[2][2] =   (in[0][0] * in[1][1] -
191                        in[0][1] * in[1][0]) * det;
192 
193 }
194 
195 void
sync()196 Warp::sync()
197 {
198 /*	cache_a=(-dest_tl[0]+dest_tr[0])/(src_br[1]-src_tl[1]);
199 	cache_b=(-dest_tl[0]+dest_bl[0])/(src_br[0]-src_tl[0]);
200 	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]));
201 	cache_d=dest_tl[0];
202 
203 	cache_e=(-dest_tl[1]+dest_tr[1])/(src_br[0]-src_tl[0]);
204 	cache_f=(-dest_tl[1]+dest_bl[1])/(src_br[1]-src_tl[1]);
205 	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]));
206 	cache_j=dest_tl[1];
207 */
208 
209 /*	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]));
210 	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]));
211 	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]));
212 
213 	matrix[0][0]=-(-dest_tl[1]+dest_tr[1])/(src_br[0]-src_tl[0]);
214 	matrix[0][1]=-(-dest_tl[1]+dest_bl[1])/(src_br[1]-src_tl[1]);
215 
216 	matrix[1][0]=-(-dest_tl[0]+dest_tr[0])/(src_br[1]-src_tl[1]);
217 	matrix[1][1]=-(-dest_tl[0]+dest_bl[0])/(src_br[0]-src_tl[0]);
218 
219 	matrix[0][2]=matrix[0][0]*dest_tl[0] + matrix[0][1]*dest_tl[1];
220 	matrix[1][2]=matrix[1][0]*dest_tl[0] + matrix[1][1]*dest_tl[1];
221 */
222 	Point src_tl=param_src_tl.get(Point());
223 	Point src_br=param_src_br.get(Point());
224 	Point dest_tl=param_dest_tl.get(Point());
225 	Point dest_tr=param_dest_tr.get(Point());
226 	Point dest_bl=param_dest_bl.get(Point());
227 	Point dest_br=param_dest_br.get(Point());
228 #define matrix tmp
229 	Real tmp[3][3];
230 
231 	const Real& x1(min(src_br[0],src_tl[0]));
232 	const Real& y1(min(src_br[1],src_tl[1]));
233 	const Real& x2(max(src_br[0],src_tl[0]));
234 	const Real& y2(max(src_br[1],src_tl[1]));
235 
236 	Real tx1(dest_bl[0]);
237 	Real ty1(dest_bl[1]);
238 	Real tx2(dest_br[0]);
239 	Real ty2(dest_br[1]);
240 	Real tx3(dest_tl[0]);
241 	Real ty3(dest_tl[1]);
242 	Real tx4(dest_tr[0]);
243 	Real ty4(dest_tr[1]);
244 
245 	if(src_br[0]<src_tl[0])
246 		swap(tx3,tx4),swap(ty3,ty4),swap(tx1,tx2),swap(ty1,ty2);
247 
248 	if(src_br[1]>src_tl[1])
249 		swap(tx3,tx1),swap(ty3,ty1),swap(tx4,tx2),swap(ty4,ty2);
250 
251 	Real scalex;
252 	Real scaley;
253 
254   scalex = scaley = 1.0;
255 
256   if ((x2 - x1) > 0)
257     scalex = 1.0 / (Real) (x2 - x1);
258 
259   if ((y2 - y1) > 0)
260     scaley = 1.0 / (Real) (y2 - y1);
261 
262   /* Determine the perspective transform that maps from
263    * the unit cube to the transformed coordinates
264    */
265   {
266     Real dx1, dx2, dx3, dy1, dy2, dy3;
267 
268     dx1 = tx2 - tx4;
269     dx2 = tx3 - tx4;
270     dx3 = tx1 - tx2 + tx4 - tx3;
271 
272     dy1 = ty2 - ty4;
273     dy2 = ty3 - ty4;
274     dy3 = ty1 - ty2 + ty4 - ty3;
275 
276     /*  Is the mapping affine?  */
277     if ((dx3 == 0.0) && (dy3 == 0.0))
278       {
279         matrix[0][0] = tx2 - tx1;
280         matrix[0][1] = tx4 - tx2;
281         matrix[0][2] = tx1;
282         matrix[1][0] = ty2 - ty1;
283         matrix[1][1] = ty4 - ty2;
284         matrix[1][2] = ty1;
285         matrix[2][0] = 0.0;
286         matrix[2][1] = 0.0;
287       }
288     else
289       {
290         Real det1, det2;
291 
292         det1 = dx3 * dy2 - dy3 * dx2;
293         det2 = dx1 * dy2 - dy1 * dx2;
294 
295         if (det1 == 0.0 && det2 == 0.0)
296           matrix[2][0] = 1.0;
297         else
298           matrix[2][0] = det1 / det2;
299 
300         det1 = dx1 * dy3 - dy1 * dx3;
301 
302         if (det1 == 0.0 && det2 == 0.0)
303           matrix[2][1] = 1.0;
304         else
305           matrix[2][1] = det1 / det2;
306 
307         matrix[0][0] = tx2 - tx1 + matrix[2][0] * tx2;
308         matrix[0][1] = tx3 - tx1 + matrix[2][1] * tx3;
309         matrix[0][2] = tx1;
310 
311         matrix[1][0] = ty2 - ty1 + matrix[2][0] * ty2;
312         matrix[1][1] = ty3 - ty1 + matrix[2][1] * ty3;
313         matrix[1][2] = ty1;
314       }
315 
316     matrix[2][2] = 1.0;
317   }
318 #undef matrix
319 
320 	Real scaletrans[3][3]={
321 			{ scalex, 0, -x1*scalex },
322 			{ 0, scaley, -y1*scaley },
323 			{ 0, 0, 1 }
324 	};
325 
326 	Real t1,t2,t3;
327 
328 	for (int i = 0; i < 3; i++)
329     {
330       t1 = tmp[i][0];
331       t2 = tmp[i][1];
332       t3 = tmp[i][2];
333 
334       for (int j = 0; j < 3; j++)
335         {
336           matrix[i][j]  = t1 * scaletrans[0][j];
337           matrix[i][j] += t2 * scaletrans[1][j];
338           matrix[i][j] += t3 * scaletrans[2][j];
339         }
340     }
341 
342 	mat3_invert(matrix, inv_matrix);
343 /*
344 	gimp_matrix3_identity  (result);
345   gimp_matrix3_translate (result, -x1, -y1);
346   gimp_matrix3_scale     (result, scalex, scaley);
347   gimp_matrix3_mult      (&matrix, result);
348 */
349 }
350 
351 bool
set_param(const String & param,const ValueBase & value)352 Warp::set_param(const String & param, const ValueBase &value)
353 {
354 	IMPORT_VALUE_PLUS(param_src_tl,sync());
355 	IMPORT_VALUE_PLUS(param_src_br,sync());
356 	IMPORT_VALUE_PLUS(param_dest_tl,sync());
357 	IMPORT_VALUE_PLUS(param_dest_tr,sync());
358 	IMPORT_VALUE_PLUS(param_dest_bl,sync());
359 	IMPORT_VALUE_PLUS(param_dest_br,sync());
360 	IMPORT_VALUE(param_clip);
361 	IMPORT_VALUE(param_horizon);
362 
363 	return false;
364 }
365 
366 ValueBase
get_param(const String & param) const367 Warp::get_param(const String &param)const
368 {
369 	EXPORT_VALUE(param_src_tl);
370 	EXPORT_VALUE(param_src_br);
371 	EXPORT_VALUE(param_dest_tl);
372 	EXPORT_VALUE(param_dest_tr);
373 	EXPORT_VALUE(param_dest_bl);
374 	EXPORT_VALUE(param_dest_br);
375 	EXPORT_VALUE(param_clip);
376 	EXPORT_VALUE(param_horizon);
377 
378 	EXPORT_NAME();
379 	EXPORT_VERSION();
380 
381 	return ValueBase();
382 }
383 
384 Layer::Vocab
get_param_vocab() const385 Warp::get_param_vocab()const
386 {
387 	Layer::Vocab ret;
388 
389 	ret.push_back(ParamDesc("src_tl")
390 		.set_local_name(_("Source TL"))
391 		.set_box("src_br")
392 		.set_description(_("Top Left corner of the source to warp"))
393 	);
394 
395 	ret.push_back(ParamDesc("src_br")
396 		.set_local_name(_("Source BR"))
397 		.set_description(_("Bottom Right corner of the source to warp"))
398 	);
399 
400 	ret.push_back(ParamDesc("dest_tl")
401 		.set_local_name(_("Dest TL"))
402 		.set_connect("dest_tr")
403 		.set_description(_("Top Left corner of the destination"))
404 	);
405 
406 	ret.push_back(ParamDesc("dest_tr")
407 		.set_local_name(_("Dest TR"))
408 		.set_connect("dest_br")
409 		.set_description(_("Top Right corner of the destination"))
410 	);
411 
412 	ret.push_back(ParamDesc("dest_br")
413 		.set_local_name(_("Dest BR"))
414 		.set_connect("dest_bl")
415 		.set_description(_("Bottom Right corner of the destination"))
416 	);
417 
418 	ret.push_back(ParamDesc("dest_bl")
419 		.set_local_name(_("Dest BL"))
420 		.set_connect("dest_tl")
421 		.set_description(_("Bottom Left corner of the destination"))
422 	);
423 
424 	ret.push_back(ParamDesc("clip")
425 		.set_local_name(_("Clip"))
426 	);
427 
428 	ret.push_back(ParamDesc("horizon")
429 		.set_local_name(_("Horizon"))
430 		.set_description(_("Height that determines the horizon in perspectives"))
431 	);
432 
433 	return ret;
434 }
435 
436 
437 class lyr_std::Warp_Trans : public Transform
438 {
439 	etl::handle<const Warp> layer;
440 public:
Warp_Trans(const Warp * x)441 	Warp_Trans(const Warp* x):Transform(x->get_guid()),layer(x) { }
442 
perform(const Vector & x) const443 	Vector perform(const Vector& x)const
444 	{
445 		return layer->transform_backward(x);
446 		//Point pos(x-layer->origin);
447 		//return Point(layer->cos_val*pos[0]-layer->sin_val*pos[1],layer->sin_val*pos[0]+layer->cos_val*pos[1])+layer->origin;
448 	}
449 
unperform(const Vector & x) const450 	Vector unperform(const Vector& x)const
451 	{
452 
453 		return layer->transform_forward(x);
454 		//Point pos(x-layer->origin);
455 		//return Point(layer->cos_val*pos[0]+layer->sin_val*pos[1],-layer->sin_val*pos[0]+layer->cos_val*pos[1])+layer->origin;
456 	}
457 
get_string() const458 	String get_string()const
459 	{
460 		return "warp";
461 	}
462 };
463 etl::handle<Transform>
get_transform() const464 Warp::get_transform()const
465 {
466 	return new Warp_Trans(this);
467 }
468 
469 Layer::Handle
hit_check(Context context,const Point & p) const470 Warp::hit_check(Context context, const Point &p)const
471 {
472 	Point src_tl=param_src_tl.get(Point());
473 	Point src_br=param_src_br.get(Point());
474 	bool clip=param_clip.get(bool());
475 
476 	Point newpos(transform_forward(p));
477 
478 	if(clip)
479 	{
480 		Rect rect(src_tl,src_br);
481 		if(!rect.is_inside(newpos))
482 			return 0;
483 	}
484 
485 	return context.hit_check(newpos);
486 }
487 
488 Color
get_color(Context context,const Point & p) const489 Warp::get_color(Context context, const Point &p)const
490 {
491 	Point src_tl=param_src_tl.get(Point());
492 	Point src_br=param_src_br.get(Point());
493 	Real horizon=param_horizon.get(Real());
494 	bool clip=param_clip.get(bool());
495 
496 	Point newpos(transform_forward(p));
497 
498 	if(clip)
499 	{
500 		Rect rect(src_tl,src_br);
501 		if(!rect.is_inside(newpos))
502 			return Color::alpha();
503 	}
504 
505 	const float z(transform_backward_z(newpos));
506 	if(z>0 && z<horizon)
507 		return context.get_color(newpos);
508 	else
509 		return Color::alpha();
510 }
511 
512 //#define ACCEL_WARP_IS_BROKEN 1
513 
514 RendDesc
get_sub_renddesc_vfunc(const RendDesc & renddesc) const515 Warp::get_sub_renddesc_vfunc(const RendDesc &renddesc) const
516 {
517 	Point dest_tl=param_dest_tl.get(Point());
518 	Point dest_tr=param_dest_tr.get(Point());
519 	Point dest_bl=param_dest_bl.get(Point());
520 	Point dest_br=param_dest_br.get(Point());
521 	Real horizon=param_horizon.get(Real());
522 	bool clip=param_clip.get(bool());
523 
524 	Point tl(renddesc.get_tl());
525 	Point br(renddesc.get_br());
526 
527 	Rect bounding_rect;
528 
529 	Rect render_rect(tl,br);
530 	Rect clip_rect(Rect::full_plane());
531 	Rect dest_rect(dest_tl,dest_br); dest_rect.expand(dest_tr).expand(dest_bl);
532 
533 	Real zoom_factor(1.0);
534 
535 	{
536 		Rect other(render_rect);
537 		if(clip)
538 			other&=dest_rect;
539 
540 		Point min(other.get_min());
541 		Point max(other.get_max());
542 
543 		bool init_point_set=false;
544 
545 		// Point trans_point[4];
546 		Point p;
547 		// Real trans_z[4];
548 		Real z,minz(10000000000000.0f),maxz(0);
549 
550 		//! \todo checking the 4 corners for 0<=z<horizon*2 and using
551 		//! only 4 corners which satisfy this condition isn't the
552 		//! right thing to do.  It's possible that none of the 4
553 		//! corners fall within that range, and yet content of the
554 		//! tile does.
555 		p=transform_forward(min);
556 		z=transform_backward_z(p);
557 		if(z>0 && z<horizon*2)
558 		{
559 			if(init_point_set)
560 				bounding_rect.expand(p);
561 			else
562 				bounding_rect=Rect(p);
563 			init_point_set=true;
564 			maxz=std::max(maxz,z);
565 			minz=std::min(minz,z);
566 		}
567 
568 		p=transform_forward(max);
569 		z=transform_backward_z(p);
570 		if(z>0 && z<horizon*2)
571 		{
572 			if(init_point_set)
573 				bounding_rect.expand(p);
574 			else
575 				bounding_rect=Rect(p);
576 			init_point_set=true;
577 			maxz=std::max(maxz,z);
578 			minz=std::min(minz,z);
579 		}
580 
581 		swap(min[1],max[1]);
582 
583 		p=transform_forward(min);
584 		z=transform_backward_z(p);
585 		if(z>0 && z<horizon*2)
586 		{
587 			if(init_point_set)
588 				bounding_rect.expand(p);
589 			else
590 				bounding_rect=Rect(p);
591 			init_point_set=true;
592 			maxz=std::max(maxz,z);
593 			minz=std::min(minz,z);
594 		}
595 
596 		p=transform_forward(max);
597 		z=transform_backward_z(p);
598 		if(z>0 && z<horizon*2)
599 		{
600 			if(init_point_set)
601 				bounding_rect.expand(p);
602 			else
603 				bounding_rect=Rect(p);
604 			init_point_set=true;
605 			maxz=std::max(maxz,z);
606 			minz=std::min(minz,z);
607 		}
608 
609 		zoom_factor=(1+(maxz-minz));
610 
611 	}
612 
613 	Point min_point(bounding_rect.get_min());
614 	Point max_point(bounding_rect.get_max());
615 
616 	// we're going to divide by the difference of these pairs soon;
617 	// if they're the same, we'll be dividing by zero, and we don't
618 	// want to do that!
619 	// \todo what should we do in this case?
620 	if (min_point[0] == max_point[0]) max_point[0] += 0.001;
621 	if (min_point[1] == max_point[1]) max_point[1] += 0.001;
622 
623 	if(tl[0]>br[0])
624 	{
625 		tl[0]=max_point[0];
626 		br[0]=min_point[0];
627 	}
628 	else
629 	{
630 		br[0]=max_point[0];
631 		tl[0]=min_point[0];
632 	}
633 	if(tl[1]>br[1])
634 	{
635 		tl[1]=max_point[1];
636 		br[1]=min_point[1];
637 	}
638 	else
639 	{
640 		br[1]=max_point[1];
641 		tl[1]=min_point[1];
642 	}
643 
644 	const int tmp_d(max(renddesc.get_w(),renddesc.get_h()));
645 	Real src_pw=(tmp_d*zoom_factor)/(br[0]-tl[0]);
646 	Real src_ph=(tmp_d*zoom_factor)/(br[1]-tl[1]);
647 
648 	RendDesc desc(renddesc);
649 	desc.clear_flags();
650 	desc.set_tl(tl);
651 	desc.set_br(br);
652 	desc.set_wh(ceil_to_int(src_pw*(br[0]-tl[0])),ceil_to_int(src_ph*(br[1]-tl[1])));
653 
654 	return desc;
655 }
656 
657 bool
accelerated_render(Context context,Surface * surface,int quality,const RendDesc & renddesc,ProgressCallback * cb) const658 Warp::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
659 {
660 	RENDER_TRANSFORMED_IF_NEED(__FILE__, __LINE__)
661 
662 	Point src_tl=param_src_tl.get(Point());
663 	Point src_br=param_src_br.get(Point());
664 	Point dest_tl=param_dest_tl.get(Point());
665 	Point dest_tr=param_dest_tr.get(Point());
666 	Point dest_bl=param_dest_bl.get(Point());
667 	Point dest_br=param_dest_br.get(Point());
668 	Real horizon=param_horizon.get(Real());
669 	bool clip=param_clip.get(bool());
670 
671 	SuperCallback stageone(cb,0,9000,10000);
672 	SuperCallback stagetwo(cb,9000,10000,10000);
673 
674 	Real pw=(renddesc.get_w())/(renddesc.get_br()[0]-renddesc.get_tl()[0]);
675 	Real ph=(renddesc.get_h())/(renddesc.get_br()[1]-renddesc.get_tl()[1]);
676 
677 	if(cb && !cb->amount_complete(0,10000))
678 		return false;
679 
680 	Point tl(renddesc.get_tl());
681 	Point br(renddesc.get_br());
682 
683 	Rect bounding_rect;
684 
685 	Rect render_rect(tl,br);
686 	Rect clip_rect(Rect::full_plane());
687 	Rect dest_rect(dest_tl,dest_br); dest_rect.expand(dest_tr).expand(dest_bl);
688 
689 	Real zoom_factor(1.0);
690 
691 	// Quick exclusion clip, if necessary
692 	if(clip && !intersect(render_rect,dest_rect))
693 	{
694 		surface->set_wh(renddesc.get_w(),renddesc.get_h());
695 		surface->clear();
696 		return true;
697 	}
698 
699 	{
700 		Rect other(render_rect);
701 		if(clip)
702 			other&=dest_rect;
703 
704 		Point min(other.get_min());
705 		Point max(other.get_max());
706 
707 		bool init_point_set=false;
708 
709 		// Point trans_point[4];
710 		Point p;
711 		// Real trans_z[4];
712 		Real z,minz(10000000000000.0f),maxz(0);
713 
714 		//! \todo checking the 4 corners for 0<=z<horizon*2 and using
715 		//! only 4 corners which satisfy this condition isn't the
716 		//! right thing to do.  It's possible that none of the 4
717 		//! corners fall within that range, and yet content of the
718 		//! tile does.
719 		p=transform_forward(min);
720 		z=transform_backward_z(p);
721 		if(z>0 && z<horizon*2)
722 		{
723 			if(init_point_set)
724 				bounding_rect.expand(p);
725 			else
726 				bounding_rect=Rect(p);
727 			init_point_set=true;
728 			maxz=std::max(maxz,z);
729 			minz=std::min(minz,z);
730 		}
731 
732 		p=transform_forward(max);
733 		z=transform_backward_z(p);
734 		if(z>0 && z<horizon*2)
735 		{
736 			if(init_point_set)
737 				bounding_rect.expand(p);
738 			else
739 				bounding_rect=Rect(p);
740 			init_point_set=true;
741 			maxz=std::max(maxz,z);
742 			minz=std::min(minz,z);
743 		}
744 
745 		swap(min[1],max[1]);
746 
747 		p=transform_forward(min);
748 		z=transform_backward_z(p);
749 		if(z>0 && z<horizon*2)
750 		{
751 			if(init_point_set)
752 				bounding_rect.expand(p);
753 			else
754 				bounding_rect=Rect(p);
755 			init_point_set=true;
756 			maxz=std::max(maxz,z);
757 			minz=std::min(minz,z);
758 		}
759 
760 		p=transform_forward(max);
761 		z=transform_backward_z(p);
762 		if(z>0 && z<horizon*2)
763 		{
764 			if(init_point_set)
765 				bounding_rect.expand(p);
766 			else
767 				bounding_rect=Rect(p);
768 			init_point_set=true;
769 			maxz=std::max(maxz,z);
770 			minz=std::min(minz,z);
771 		}
772 
773 		if(!init_point_set)
774 		{
775 			surface->set_wh(renddesc.get_w(),renddesc.get_h());
776 			surface->clear();
777 			return true;
778 		}
779 		zoom_factor=(1+(maxz-minz));
780 
781 	}
782 
783 #ifdef ACCEL_WARP_IS_BROKEN
784 	return Layer::accelerated_render(context,surface,quality,renddesc, cb);
785 #else
786 
787 	/*swap(tl[1],br[1]);
788 	bounding_rect
789 		.expand(transform_forward(tl))
790 		.expand(transform_forward(br))
791 	;
792 	swap(tl[1],br[1]);*/
793 
794 	//warning("given window: [%f,%f]-[%f,%f] %dx%d",tl[0],tl[1],br[0],br[1],renddesc.get_w(),renddesc.get_h());
795 	//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]);
796 
797 	// If we are clipping, then go ahead and clip to the
798 	// source rectangle
799 	if(clip)
800 		clip_rect&=Rect(src_tl,src_br);
801 
802 	// Bound ourselves to the bounding rectangle of
803 	// what is under us
804 	clip_rect&=context.get_full_bounding_rect();//.expand_x(abs(zoom_factor/pw)).expand_y(abs(zoom_factor/ph));
805 
806 	bounding_rect&=clip_rect;
807 
808 	Point min_point(bounding_rect.get_min());
809 	Point max_point(bounding_rect.get_max());
810 
811 	// we're going to divide by the difference of these pairs soon;
812 	// if they're the same, we'll be dividing by zero, and we don't
813 	// want to do that!
814 	// \todo what should we do in this case?
815 	if (min_point[0] == max_point[0]) max_point[0] += 0.001;
816 	if (min_point[1] == max_point[1]) max_point[1] += 0.001;
817 
818 	if(tl[0]>br[0])
819 	{
820 		tl[0]=max_point[0];
821 		br[0]=min_point[0];
822 	}
823 	else
824 	{
825 		br[0]=max_point[0];
826 		tl[0]=min_point[0];
827 	}
828 	if(tl[1]>br[1])
829 	{
830 		tl[1]=max_point[1];
831 		br[1]=min_point[1];
832 	}
833 	else
834 	{
835 		br[1]=max_point[1];
836 		tl[1]=min_point[1];
837 	}
838 
839 
840 
841 	const int tmp_d(max(renddesc.get_w(),renddesc.get_h()));
842 	Real src_pw=(tmp_d*zoom_factor)/(br[0]-tl[0]);
843 	Real src_ph=(tmp_d*zoom_factor)/(br[1]-tl[1]);
844 
845 
846 	RendDesc desc(renddesc);
847 	desc.clear_flags();
848 	//desc.set_flags(RendDesc::PX_ASPECT);
849 	desc.set_tl(tl);
850 	desc.set_br(br);
851 	desc.set_wh(ceil_to_int(src_pw*(br[0]-tl[0])),ceil_to_int(src_ph*(br[1]-tl[1])));
852 
853 	//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());
854 	if(desc.get_w()==0 && desc.get_h()==0)
855 	{
856 		surface->set_wh(renddesc.get_w(),renddesc.get_h());
857 		surface->clear();
858 		return true;
859 	}
860 
861 	// Recalculate the pixel widths for the src renddesc
862 	src_pw=(desc.get_w())/(desc.get_br()[0]-desc.get_tl()[0]);
863 	src_ph=(desc.get_h())/(desc.get_br()[1]-desc.get_tl()[1]);
864 
865 
866 	Surface source;
867 	source.set_wh(desc.get_w(),desc.get_h());
868 
869 	if(!context.accelerated_render(&source,quality,desc,&stageone))
870 		return false;
871 
872 	surface->set_wh(renddesc.get_w(),renddesc.get_h());
873 	surface->clear();
874 
875 	Surface::pen pen(surface->begin());
876 
877 	if(quality<=4)
878 	{
879 		// CUBIC
880 		int x,y;
881 		float u,v;
882 		Point point,tmp;
883 		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)
884 		{
885 			for(x=0,point[0]=renddesc.get_tl()[0];x<surface->get_w();x++,pen.inc_x(),point[0]+=1.0/pw)
886 			{
887 				tmp=transform_forward(point);
888 				const float z(transform_backward_z(tmp));
889 				if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
890 				{
891 					(*surface)[y][x]=Color::alpha();
892 					continue;
893 				}
894 
895 				u=(tmp[0]-tl[0])*src_pw;
896 				v=(tmp[1]-tl[1])*src_ph;
897 
898 				if(u<0 || v<0 || u>=source.get_w() || v>=source.get_h() || std::isnan(u) || std::isnan(v))
899 					(*surface)[y][x]=context.get_color(tmp);
900 				else
901 					(*surface)[y][x]=source.cubic_sample(u,v);
902 			}
903 			if((y&31)==0 && cb)
904 			{
905 				if(!stagetwo.amount_complete(y,surface->get_h()))
906 					return false;
907 			}
908 		}
909 	}
910 	else
911 	if(quality<=6)
912 	{
913 		// INTERPOLATION_LINEAR
914 		int x,y;
915 		float u,v;
916 		Point point,tmp;
917 		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)
918 		{
919 			for(x=0,point[0]=renddesc.get_tl()[0];x<surface->get_w();x++,pen.inc_x(),point[0]+=1.0/pw)
920 			{
921 				tmp=transform_forward(point);
922 				const float z(transform_backward_z(tmp));
923 				if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
924 				{
925 					(*surface)[y][x]=Color::alpha();
926 					continue;
927 				}
928 
929 				u=(tmp[0]-tl[0])*src_pw;
930 				v=(tmp[1]-tl[1])*src_ph;
931 
932 				if(u<0 || v<0 || u>=source.get_w() || v>=source.get_h() || std::isnan(u) || std::isnan(v))
933 					(*surface)[y][x]=context.get_color(tmp);
934 				else
935 					(*surface)[y][x]=source.linear_sample(u,v);
936 			}
937 			if((y&31)==0 && cb)
938 			{
939 				if(!stagetwo.amount_complete(y,surface->get_h()))
940 					return false;
941 			}
942 		}
943 	}
944 	else
945 	{
946 		// NEAREST_NEIGHBOR
947 		int x,y;
948 		float u,v;
949 		Point point,tmp;
950 		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)
951 		{
952 			for(x=0,point[0]=renddesc.get_tl()[0];x<surface->get_w();x++,pen.inc_x(),point[0]+=1.0/pw)
953 			{
954 				tmp=transform_forward(point);
955 				const float z(transform_backward_z(tmp));
956 				if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
957 				{
958 					(*surface)[y][x]=Color::alpha();
959 					continue;
960 				}
961 
962 				u=(tmp[0]-tl[0])*src_pw;
963 				v=(tmp[1]-tl[1])*src_ph;
964 
965 				if(u<0 || v<0 || u>=source.get_w() || v>=source.get_h() || std::isnan(u) || std::isnan(v))
966 					(*surface)[y][x]=context.get_color(tmp);
967 				else
968 					//pen.set_value(source[v][u]);
969 					(*surface)[y][x]=source[floor_to_int(v)][floor_to_int(u)];
970 			}
971 			if((y&31)==0 && cb)
972 			{
973 				if(!stagetwo.amount_complete(y,surface->get_h()))
974 					return false;
975 			}
976 		}
977 	}
978 
979 #endif
980 
981 	if(cb && !cb->amount_complete(10000,10000)) return false;
982 
983 	return true;
984 }
985 
986 
987 //////////
988 bool
accelerated_cairorender(Context context,cairo_t * cr,int quality,const RendDesc & renddesc_,ProgressCallback * cb) const989 Warp::accelerated_cairorender(Context context, cairo_t *cr, int quality, const RendDesc &renddesc_, ProgressCallback *cb)const
990 {
991 	Point src_tl=param_src_tl.get(Point());
992 	Point src_br=param_src_br.get(Point());
993 	Point dest_tl=param_dest_tl.get(Point());
994 	Point dest_tr=param_dest_tr.get(Point());
995 	Point dest_bl=param_dest_bl.get(Point());
996 	Point dest_br=param_dest_br.get(Point());
997 	Real horizon=param_horizon.get(Real());
998 	bool clip=param_clip.get(bool());
999 
1000 	SuperCallback stageone(cb,0,9000,10000);
1001 	SuperCallback stagetwo(cb,9000,10000,10000);
1002 
1003 
1004 	RendDesc renddesc(renddesc_);
1005 	// Untransform the render desc
1006 	if(!cairo_renddesc_untransform(cr, renddesc))
1007 		return false;
1008 
1009 	Real pw=(renddesc.get_w())/(renddesc.get_br()[0]-renddesc.get_tl()[0]);
1010 	Real ph=(renddesc.get_h())/(renddesc.get_br()[1]-renddesc.get_tl()[1]);
1011 
1012 	if(cb && !cb->amount_complete(0,10000))
1013 		return false;
1014 
1015 	Point tl(renddesc.get_tl());
1016 	Point br(renddesc.get_br());
1017 
1018 	Rect bounding_rect;
1019 
1020 	Rect render_rect(tl,br);
1021 	Rect clip_rect(Rect::full_plane());
1022 	Rect dest_rect(dest_tl,dest_br); dest_rect.expand(dest_tr).expand(dest_bl);
1023 
1024 	Real zoom_factor(1.0);
1025 
1026 	// Quick exclusion clip, if necessary
1027 	if(clip && !intersect(render_rect,dest_rect))
1028 	{
1029 		cairo_save(cr);
1030 		cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
1031 		cairo_paint(cr);
1032 		cairo_restore(cr);
1033 		return true;
1034 	}
1035 
1036 	{
1037 		Rect other(render_rect);
1038 		if(clip)
1039 			other&=dest_rect;
1040 
1041 		Point min(other.get_min());
1042 		Point max(other.get_max());
1043 
1044 		bool init_point_set=false;
1045 
1046 		// Point trans_point[4];
1047 		Point p;
1048 		// Real trans_z[4];
1049 		Real z,minz(10000000000000.0f),maxz(0);
1050 
1051 		//! \todo checking the 4 corners for 0<=z<horizon*2 and using
1052 		//! only 4 corners which satisfy this condition isn't the
1053 		//! right thing to do.  It's possible that none of the 4
1054 		//! corners fall within that range, and yet content of the
1055 		//! tile does.
1056 		p=transform_forward(min);
1057 		z=transform_backward_z(p);
1058 		if(z>0 && z<horizon*2)
1059 		{
1060 			if(init_point_set)
1061 				bounding_rect.expand(p);
1062 			else
1063 				bounding_rect=Rect(p);
1064 			init_point_set=true;
1065 			maxz=std::max(maxz,z);
1066 			minz=std::min(minz,z);
1067 		}
1068 
1069 		p=transform_forward(max);
1070 		z=transform_backward_z(p);
1071 		if(z>0 && z<horizon*2)
1072 		{
1073 			if(init_point_set)
1074 				bounding_rect.expand(p);
1075 			else
1076 				bounding_rect=Rect(p);
1077 			init_point_set=true;
1078 			maxz=std::max(maxz,z);
1079 			minz=std::min(minz,z);
1080 		}
1081 
1082 		swap(min[1],max[1]);
1083 
1084 		p=transform_forward(min);
1085 		z=transform_backward_z(p);
1086 		if(z>0 && z<horizon*2)
1087 		{
1088 			if(init_point_set)
1089 				bounding_rect.expand(p);
1090 			else
1091 				bounding_rect=Rect(p);
1092 			init_point_set=true;
1093 			maxz=std::max(maxz,z);
1094 			minz=std::min(minz,z);
1095 		}
1096 
1097 		p=transform_forward(max);
1098 		z=transform_backward_z(p);
1099 		if(z>0 && z<horizon*2)
1100 		{
1101 			if(init_point_set)
1102 				bounding_rect.expand(p);
1103 			else
1104 				bounding_rect=Rect(p);
1105 			init_point_set=true;
1106 			maxz=std::max(maxz,z);
1107 			minz=std::min(minz,z);
1108 		}
1109 
1110 		if(!init_point_set)
1111 		{
1112 			cairo_save(cr);
1113 			cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
1114 			cairo_paint(cr);
1115 			cairo_restore(cr);
1116 			return true;
1117 		}
1118 		zoom_factor=(1+(maxz-minz));
1119 
1120 	}
1121 
1122 #ifdef ACCEL_WARP_IS_BROKEN
1123 	return Layer::accelerated_cairorender(context,cr,quality,renddesc, cb);
1124 #else
1125 
1126 	/*swap(tl[1],br[1]);
1127 	 bounding_rect
1128 	 .expand(transform_forward(tl))
1129 	 .expand(transform_forward(br))
1130 	 ;
1131 	 swap(tl[1],br[1]);*/
1132 
1133 	//warning("given window: [%f,%f]-[%f,%f] %dx%d",tl[0],tl[1],br[0],br[1],renddesc.get_w(),renddesc.get_h());
1134 	//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]);
1135 
1136 	// If we are clipping, then go ahead and clip to the
1137 	// source rectangle
1138 	if(clip)
1139 		clip_rect&=Rect(src_tl,src_br);
1140 
1141 	// Bound ourselves to the bounding rectangle of
1142 	// what is under us
1143 	clip_rect&=context.get_full_bounding_rect();//.expand_x(abs(zoom_factor/pw)).expand_y(abs(zoom_factor/ph));
1144 
1145 	bounding_rect&=clip_rect;
1146 
1147 	Point min_point(bounding_rect.get_min());
1148 	Point max_point(bounding_rect.get_max());
1149 
1150 	// we're going to divide by the difference of these pairs soon;
1151 	// if they're the same, we'll be dividing by zero, and we don't
1152 	// want to do that!
1153 	// \todo what should we do in this case?
1154 	if (min_point[0] == max_point[0]) max_point[0] += 0.001;
1155 	if (min_point[1] == max_point[1]) max_point[1] += 0.001;
1156 
1157 	if(tl[0]>br[0])
1158 	{
1159 		tl[0]=max_point[0];
1160 		br[0]=min_point[0];
1161 	}
1162 	else
1163 	{
1164 		br[0]=max_point[0];
1165 		tl[0]=min_point[0];
1166 	}
1167 	if(tl[1]>br[1])
1168 	{
1169 		tl[1]=max_point[1];
1170 		br[1]=min_point[1];
1171 	}
1172 	else
1173 	{
1174 		br[1]=max_point[1];
1175 		tl[1]=min_point[1];
1176 	}
1177 
1178 	const int tmp_d(max(renddesc.get_w(),renddesc.get_h()));
1179 	Real src_pw=(tmp_d*zoom_factor)/(br[0]-tl[0]);
1180 	Real src_ph=(tmp_d*zoom_factor)/(br[1]-tl[1]);
1181 
1182 
1183 	RendDesc desc(renddesc);
1184 	desc.clear_flags();
1185 	//desc.set_flags(RendDesc::PX_ASPECT);
1186 	desc.set_tl(tl);
1187 	desc.set_br(br);
1188 	desc.set_wh(ceil_to_int(src_pw*(br[0]-tl[0])),ceil_to_int(src_ph*(br[1]-tl[1])));
1189 
1190 	//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());
1191 	if(desc.get_w()==0 && desc.get_h()==0)
1192 	{
1193 		cairo_save(cr);
1194 		cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
1195 		cairo_paint(cr);
1196 		cairo_restore(cr);
1197 		return true;
1198 	}
1199 
1200 	// Recalculate the pixel widths for the src renddesc
1201 	src_pw=(desc.get_w())/(desc.get_br()[0]-desc.get_tl()[0]);
1202 	src_ph=(desc.get_h())/(desc.get_br()[1]-desc.get_tl()[1]);
1203 
1204 	cairo_surface_t* source=cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, desc.get_w(),desc.get_h());
1205 	cairo_surface_t* surface=cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA,renddesc.get_w(), renddesc.get_h());
1206 	cairo_t* subcr=cairo_create(source);
1207 	cairo_scale(subcr, 1/desc.get_pw(), 1/desc.get_ph());
1208 	cairo_translate(subcr, -desc.get_tl()[0], -desc.get_tl()[1]);
1209 
1210 	if(!context.accelerated_cairorender(subcr,quality,desc,&stageone))
1211 		return false;
1212 
1213 	cairo_destroy(subcr);
1214 
1215 	int surfacew, surfaceh, sourcew, sourceh;
1216 
1217 	CairoSurface csurface(surface);
1218 	CairoSurface csource(source);
1219 
1220 	csurface.map_cairo_image();
1221 	csource.map_cairo_image();
1222 
1223 	surfacew=csurface.get_w();
1224 	surfaceh=csurface.get_h();
1225 	sourcew=csource.get_w();
1226 	sourceh=csource.get_h();
1227 
1228 	CairoSurface::pen pen(csurface.begin());
1229 
1230 	// Do the warp
1231 	{
1232 		int x,y;
1233 		float u,v;
1234 		Point point,tmp;
1235 		for(y=0,point[1]=renddesc.get_tl()[1];y<surfaceh;y++,pen.inc_y(),pen.dec_x(x),point[1]+=1.0/ph)
1236 		{
1237 			for(x=0,point[0]=renddesc.get_tl()[0];x<surfacew;x++,pen.inc_x(),point[0]+=1.0/pw)
1238 			{
1239 				tmp=transform_forward(point);
1240 				const float z(transform_backward_z(tmp));
1241 				if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
1242 				{
1243 					csurface[y][x]=Color::alpha();
1244 					continue;
1245 				}
1246 
1247 				u=(tmp[0]-tl[0])*src_pw;
1248 				v=(tmp[1]-tl[1])*src_ph;
1249 
1250 				if(u<0 || v<0 || u>=sourcew || v>=sourceh || std::isnan(u) || std::isnan(v))
1251 					csurface[y][x]=context.get_cairocolor(tmp);
1252 				else
1253 				{
1254 					// CUBIC
1255 					if(quality<=4)
1256 						csurface[y][x]=csource.cubic_sample_cooked(u,v);
1257 					// INTEPOLATION_LINEAR
1258 					else if(quality<=6)
1259 						csurface[y][x]=csource.linear_sample_cooked(u,v);
1260 					else
1261 						// NEAREST_NEIGHBOR
1262 						csurface[y][x]=csource[floor_to_int(v)][floor_to_int(u)];
1263 				}
1264 			}
1265 			if((y&31)==0 && cb)
1266 			{
1267 				if(!stagetwo.amount_complete(y,surfaceh))
1268 					return false;
1269 			}
1270 		}
1271 	}
1272 
1273 #endif
1274 
1275 	if(cb && !cb->amount_complete(10000,10000)) return false;
1276 
1277 	csurface.unmap_cairo_image();
1278 	csource.unmap_cairo_image();
1279 	cairo_surface_destroy(source);
1280 
1281 	cairo_save(cr);
1282 
1283 	cairo_translate(cr, renddesc.get_tl()[0], renddesc.get_tl()[1]);
1284 	cairo_scale(cr, renddesc.get_pw(), renddesc.get_ph());
1285 	cairo_set_source_surface(cr, surface, 0, 0);
1286 	cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1287 	cairo_paint(cr);
1288 
1289 	cairo_restore(cr);
1290 
1291 	cairo_surface_destroy(surface);
1292 	return true;
1293 }
1294 
1295 Rect
get_bounding_rect() const1296 Warp::get_bounding_rect()const
1297 {
1298 	return Rect::full_plane();
1299 }
1300 
1301 Rect
get_full_bounding_rect(Context context) const1302 Warp::get_full_bounding_rect(Context context)const
1303 {
1304 //	return Rect::full_plane();
1305 
1306 	Point src_tl=param_src_tl.get(Point());
1307 	Point src_br=param_src_br.get(Point());
1308 	bool clip=param_clip.get(bool());
1309 
1310 	Rect under(context.get_full_bounding_rect());
1311 
1312 	if(clip)
1313 	{
1314 		under&=Rect(src_tl,src_br);
1315 	}
1316 
1317 	return get_transform()->perform(under);
1318 
1319 	/*
1320 	Rect under(context.get_full_bounding_rect());
1321 	Rect ret(Rect::zero());
1322 
1323 	if(under.area()==HUGE_VAL)
1324 		return Rect::full_plane();
1325 
1326 	ret.expand(
1327 		transform_backward(
1328 			under.get_min()
1329 		)
1330 	);
1331 	ret.expand(
1332 		transform_backward(
1333 			under.get_max()
1334 		)
1335 	);
1336 	ret.expand(
1337 		transform_backward(
1338 			Vector(
1339 				under.get_min()[0],
1340 				under.get_max()[1]
1341 			)
1342 		)
1343 	);
1344 	ret.expand(
1345 		transform_backward(
1346 			Vector(
1347 				under.get_max()[0],
1348 				under.get_min()[1]
1349 			)
1350 		)
1351 	);
1352 
1353 	if(ret.area()==HUGE_VAL)
1354 		return Rect::full_plane();
1355 
1356 	return ret;
1357 	*/
1358 }
1359