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 ¶m)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