1 /* === S Y N F I G ========================================================= */
2 /*! \file sphere_distort.cpp
3 ** \brief Implementation of the "Spherize" 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 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 /* ========================================================================= */
24
25 /* === H E A D E R S ======================================================= */
26
27 #ifdef USING_PCH
28 # include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include "sphere_distort.h"
35
36 #include <synfig/localization.h>
37 #include <synfig/general.h>
38
39 #include <synfig/string.h>
40 #include <synfig/time.h>
41 #include <synfig/context.h>
42 #include <synfig/paramdesc.h>
43 #include <synfig/renddesc.h>
44 #include <synfig/surface.h>
45 #include <synfig/value.h>
46 #include <synfig/valuenode.h>
47 #include <synfig/transform.h>
48 #include <synfig/cairo_renddesc.h>
49
50 #include <synfig/curve_helper.h>
51
52 #endif
53
54 /* === U S I N G =========================================================== */
55
56 using namespace std;
57 using namespace etl;
58 using namespace synfig;
59 using namespace modules;
60 using namespace lyr_std;
61
62 /* === M A C R O S ========================================================= */
63
64 #ifndef PI
65 const double PI = 3.14159265;
66 #endif
67
68 enum
69 {
70 TYPE_NORMAL = 0,
71 TYPE_DISTH = 1, //axe the horizontal axis
72 TYPE_DISTV = 2, //axe the vertical axis
73 N_TYPES
74 };
75
76 /* === G L O B A L S ======================================================= */
77
78 SYNFIG_LAYER_INIT(Layer_SphereDistort);
79 SYNFIG_LAYER_SET_NAME(Layer_SphereDistort,"spherize");
80 SYNFIG_LAYER_SET_LOCAL_NAME(Layer_SphereDistort,N_("Spherize"));
81 SYNFIG_LAYER_SET_CATEGORY(Layer_SphereDistort,N_("Distortions"));
82 SYNFIG_LAYER_SET_VERSION(Layer_SphereDistort,"0.2");
83 SYNFIG_LAYER_SET_CVS_ID(Layer_SphereDistort,"$Id$");
84
85 /* === P R O C E D U R E S ================================================= */
86
87 /* === M E T H O D S ======================================================= */
88
89 /* === E N T R Y P O I N T ================================================= */
90
Layer_SphereDistort()91 Layer_SphereDistort::Layer_SphereDistort():
92 param_center(ValueBase(Vector(0,0))),
93 param_radius(ValueBase(double(1))),
94 param_amount(ValueBase(double(1))),
95 param_type(ValueBase(int(TYPE_NORMAL))),
96 param_clip(ValueBase(false))
97 {
98 SET_INTERPOLATION_DEFAULTS();
99 SET_STATIC_DEFAULTS();
100 }
101
102
103 bool
set_param(const String & param,const ValueBase & value)104 Layer_SphereDistort::set_param(const String & param, const ValueBase &value)
105 {
106 IMPORT_VALUE_PLUS(param_center,sync());
107 IMPORT_VALUE_PLUS(param_radius,sync());
108 IMPORT_VALUE(param_type);
109 IMPORT_VALUE(param_amount);
110 IMPORT_VALUE(param_clip);
111
112 if(param=="percent" && param_amount.get_type()==value.get_type())
113 return set_param("amount", value);
114
115 return Layer::set_param(param,value);
116 }
117
118 ValueBase
get_param(const String & param) const119 Layer_SphereDistort::get_param(const String ¶m)const
120 {
121 EXPORT_VALUE(param_center);
122 EXPORT_VALUE(param_radius);
123 EXPORT_VALUE(param_type);
124 EXPORT_VALUE(param_amount);
125 EXPORT_VALUE(param_clip);
126 if(param=="percent")
127 return get_param("amount");
128
129 EXPORT_NAME();
130 EXPORT_VERSION();
131
132 return Layer::get_param(param);
133 }
134
135 void
sync()136 Layer_SphereDistort::sync()
137 {
138 // Remove me?
139 }
140
141 Layer::Vocab
get_param_vocab() const142 Layer_SphereDistort::get_param_vocab()const
143 {
144 Layer::Vocab ret;
145
146 ret.push_back(ParamDesc("center")
147 .set_local_name(_("Position"))
148 .set_description(_("Where the sphere distortion is centered"))
149 );
150
151 ret.push_back(ParamDesc("radius")
152 .set_local_name(_("Radius"))
153 .set_origin("center")
154 .set_is_distance()
155 .set_description(_("The size of the sphere distortion"))
156 );
157
158 ret.push_back(ParamDesc("amount")
159 .set_local_name(_("Amount"))
160 .set_is_distance(false)
161 .set_description(_("The distortion intensity (negative values inverts effect)"))
162 );
163
164 ret.push_back(ParamDesc("clip")
165 .set_local_name(_("Clip"))
166 .set_description(_("When cheked, the area outside the Radius are not distorted"))
167 );
168
169 ret.push_back(ParamDesc("type")
170 .set_local_name(_("Distort Type"))
171 .set_description(_("The direction of the distortion"))
172 .set_hint("enum")
173 .add_enum_value(TYPE_NORMAL,"normal",_("Spherize"))
174 .add_enum_value(TYPE_DISTH,"honly",_("Vertical Bar"))
175 .add_enum_value(TYPE_DISTV,"vonly",_("Horizontal Bar"))
176 );
177
178 return ret;
179 }
180
181 /*
182 Spherical Distortion: maps an image onto a ellipsoid of some sort
183
184 so the image coordinate (i.e. distance away from the center)
185 will determine how things get mapped
186
187 so with the radius and position the mapping would go as follows
188
189 r = (pos - center) / radius clamped to [-1,1]
190
191 if it's outside of that range then it's not distorted
192 but if it's inside of that range then it goes as follows
193
194 angle = r * pi/2 (-pi/2,pi/2)
195
196 newr = cos(angle)*radius
197
198 the inverse of this is (which is actually what we'd be transforming it from
199
200
201 */
202
spherify(float f)203 inline float spherify(float f)
204 {
205 if(f > -1 && f < 1 && f!=0)
206 return sinf(f*(PI/2));
207 else return f;
208 }
209
unspherify(float f)210 inline float unspherify(float f)
211 {
212 if(f > -1 && f < 1 && f!=0)
213 return asin(f)/(PI/2);
214 else return f;
215 }
216
sphtrans(const Point & p,const Point & center,const float & radius,const Real & percent,int type,bool & clipped)217 Point sphtrans(const Point &p, const Point ¢er, const float &radius,
218 const Real &percent, int type, bool& clipped)
219 {
220 const Vector v = (p - center) / radius;
221
222 Point newp = p;
223 const float t = percent;
224
225 clipped=false;
226
227 if(type == TYPE_NORMAL)
228 {
229 const float m = v.mag();
230 float lerp(0);
231
232 if(m <= -1 || m >= 1)
233 {
234 clipped=true;
235 return newp;
236 }else
237 if(m==0)
238 return newp;
239 else
240 if(t > 0)
241 {
242 lerp = (t*unspherify(m) + (1-t)*m);
243 }else if(t < 0)
244 {
245 lerp = ((1+t)*m - t*spherify(m));
246 }else lerp = m;
247
248 const float d = lerp*radius;
249 newp = center + v*(d/m);
250 }
251
252 else if(type == TYPE_DISTH)
253 {
254 float lerp(0);
255 if(v[0] <= -1 || v[0] >= 1)
256 {
257 clipped=true;
258 return newp;
259 }else
260 if(v[0]==0)
261 return newp;
262 else
263 if(t > 0)
264 {
265 lerp = (t*unspherify(v[0]) + (1-t)*v[0]);
266 }else if(t < 0)
267 {
268 lerp = ((1+t)*v[0] - t*spherify(v[0]));
269 }else lerp = v[0];
270
271 newp[0] = center[0] + lerp*radius;
272 }
273
274 else if(type == TYPE_DISTV)
275 {
276 float lerp(0);
277 if(v[1] <= -1 || v[1] >= 1)
278 {
279 clipped=true;
280 return newp;
281 }
282 else
283 if(v[1]==0)
284 return newp;
285 else
286 if(t > 0)
287 {
288 lerp = (t*unspherify(v[1]) + (1-t)*v[1]);
289 }else if(t < 0)
290 {
291 lerp = ((1+t)*v[1] - t*spherify(v[1]));
292 }else lerp = v[1];
293
294 newp[1] = center[1] + lerp*radius;
295 }
296
297 return newp;
298 }
299
sphtrans(const Point & p,const Point & center,const Real & radius,const Real & percent,int type)300 inline Point sphtrans(const Point &p, const Point ¢er, const Real &radius,
301 const Real &percent, int type)
302 {
303 bool tmp;
304 return sphtrans(p, center, radius, percent, type, tmp);
305 }
306
307 Layer::Handle
hit_check(Context context,const Point & pos) const308 Layer_SphereDistort::hit_check(Context context, const Point &pos)const
309 {
310 Vector center=param_center.get(Vector());
311 double radius=param_radius.get(double());
312 double percent=param_amount.get(double());
313 int type=param_type.get(int());
314 bool clip=param_clip.get(bool());
315
316 bool clipped;
317 Point point(sphtrans(pos,center,radius,percent,type,clipped));
318 if(clip && clipped)
319 return 0;
320 return context.hit_check(point);
321 }
322
323 Color
get_color(Context context,const Point & pos) const324 Layer_SphereDistort::get_color(Context context, const Point &pos)const
325 {
326 Vector center=param_center.get(Vector());
327 double radius=param_radius.get(double());
328 double percent=param_amount.get(double());
329 int type=param_type.get(int());
330 bool clip=param_clip.get(bool());
331
332 bool clipped;
333 Point point(sphtrans(pos,center,radius,percent,type,clipped));
334 if(clip && clipped)
335 return Color::alpha();
336 return context.get_color(point);
337 }
338
339 RendDesc
get_sub_renddesc_vfunc(const RendDesc & renddesc) const340 Layer_SphereDistort::get_sub_renddesc_vfunc(const RendDesc &renddesc) const
341 {
342 RendDesc desc(renddesc);
343 Real pw = desc.get_pw();
344 Real ph = desc.get_ph();
345 desc.set_tl(Vector(-10.0, -10.0));
346 desc.set_br(Vector( 10.0, 10.0));
347 desc.set_wh(
348 (int)approximate_ceil(fabs((desc.get_br()[0] - desc.get_tl()[0])/pw)),
349 (int)approximate_ceil(fabs((desc.get_br()[1] - desc.get_tl()[1])/ph)) );
350 return desc;
351 }
352
353 #if 1
354 bool
accelerated_render(Context context,Surface * surface,int quality,const RendDesc & renddesc,ProgressCallback * cb) const355 Layer_SphereDistort::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
356 {
357 RENDER_TRANSFORMED_IF_NEED(__FILE__, __LINE__)
358
359 /* Things to consider:
360 1) Block expansion for distortion (ouch... quality level??)
361 2) Bounding box clipping
362 3) Super sampling for better visual quality (based on the quality level?)
363 4) Interpolation type for sampling (based on quality level?)
364
365 //things to defer until after
366 super sampling, non-linear interpolation
367 */
368
369 //bounding box reject
370 Vector center=param_center.get(Vector());
371 double radius=param_radius.get(double());
372 double percent=param_amount.get(double());
373 int type=param_type.get(int());
374 bool clip=param_clip.get(bool());
375 {
376 Rect sphr;
377
378 sphr.set_point(center[0]-radius,center[1]-radius);
379 sphr.expand(center[0]+radius,center[1]+radius);
380
381 //get the bounding box of the transform
382 Rect windr;
383
384 //and the bounding box of the rendering
385 windr.set_point(renddesc.get_tl()[0],renddesc.get_tl()[1]);
386 windr.expand(renddesc.get_br()[0],renddesc.get_br()[1]);
387
388 //test bounding boxes for collision
389 if( (type == TYPE_NORMAL && !intersect(sphr,windr)) ||
390 (type == TYPE_DISTH && (sphr.minx >= windr.maxx || windr.minx >= sphr.maxx)) ||
391 (type == TYPE_DISTV && (sphr.miny >= windr.maxy || windr.miny >= sphr.maxy)) )
392 {
393 //warning("Spherize: Bounding box reject");
394 if (clip)
395 {
396 surface->set_wh(renddesc.get_w(), renddesc.get_h());
397 surface->clear();
398 return true;
399 }
400 else
401 return context.accelerated_render(surface,quality,renddesc,cb);
402 }
403
404 //warning("Spherize: Bounding box accept");
405 }
406
407 //Ok, so we overlap some... now expand the window for rendering
408 RendDesc r = renddesc;
409 Surface background;
410 Real pw = renddesc.get_pw(),ph = renddesc.get_ph();
411
412 int nl=0,nt=0,nr=0,nb=0, nw=0,nh=0;
413 Point tl = renddesc.get_tl(), br = renddesc.get_br();
414
415 {
416 //must enlarge window by pixel coordinates so go!
417
418 //need to figure out closest and farthest point and distort THOSE
419
420 Point origin[4] = {tl,tl,br,br};
421 Vector v[4] = {Vector(0,br[1]-tl[1]),
422 Vector(br[0]-tl[0],0),
423 Vector(0,tl[1]-br[1]),
424 Vector(tl[0]-br[0],0)};
425
426 Point close(0,0);
427 Real t = 0;
428 Rect expandr(tl,br);
429
430 //expandr.set_point(tl[0],tl[1]);
431 //expandr.expand(br[0],br[1]);
432
433 //warning("Spherize: Loop through lines and stuff");
434 for(int i=0; i<4; ++i)
435 {
436 //warning("Spherize: %d", i);
437 Vector p_o = center-origin[i];
438
439 //project onto left line
440 t = (p_o*v[i])/v[i].mag_squared();
441
442 //clamp
443 if(t < 0) t = 0; if(t > 1) t = 1;
444
445 close = origin[i] + v[i]*t;
446
447 //now get transforms and expand the rectangle to accommodate
448 Point p = sphtrans(close,center,radius,percent,type);
449 expandr.expand(p[0],p[1]);
450 p = sphtrans(origin[i],center,radius,percent,type);
451 expandr.expand(p[0],p[1]);
452 p = sphtrans(origin[i]+v[i],center,radius,percent,type);
453 expandr.expand(p[0],p[1]);
454 }
455
456 /*warning("Spherize: Bounding box (%f,%f)-(%f,%f)",
457 expandr.minx,expandr.miny,expandr.maxx,expandr.maxy);*/
458
459 //now that we have the bounding rectangle of ALL the pixels (should be...)
460 //order it so that it's in the same orientation as the tl,br pair
461
462 //warning("Spherize: Organize like tl,br");
463 Point ntl(0,0),nbr(0,0);
464
465 //sort x
466 if(tl[0] < br[0])
467 {
468 ntl[0] = expandr.minx;
469 nbr[0] = expandr.maxx;
470 }
471 else
472 {
473 ntl[0] = expandr.maxx;
474 nbr[0] = expandr.minx;
475 }
476
477 //sort y
478 if(tl[1] < br[1])
479 {
480 ntl[1] = expandr.miny;
481 nbr[1] = expandr.maxy;
482 }
483 else
484 {
485 ntl[1] = expandr.maxy;
486 nbr[1] = expandr.miny;
487 }
488
489 //now expand the window as needed
490 Vector temp = ntl-tl;
491
492 //pixel offset
493 nl = (int)(temp[0]/pw)-1;
494 nt = (int)(temp[1]/ph)-1;
495
496 temp = nbr - br;
497 nr = (int)(temp[0]/pw)+1;
498 nb = (int)(temp[1]/ph)+1;
499
500 nw = renddesc.get_w() + nr - nl;
501 nh = renddesc.get_h() + nb - nt;
502
503 //warning("Spherize: Setting subwindow (%d,%d) (%d,%d) (%d,%d)",nl,nt,nr,nb,nw,nh);
504 r.set_subwindow(nl,nt,nw,nh);
505
506 /*r = renddesc;
507 nw = r.get_w(), nh = r.get_h();
508 nl = 0, nt = 0;*/
509 }
510
511 //warning("Spherize: render background");
512 if(!context.accelerated_render(&background,quality,r,cb))
513 {
514 warning("SphereDistort: Layer below failed");
515 return false;
516 }
517
518 //now distort and check to make sure we aren't overshooting our bounds here
519 int w = renddesc.get_w(), h = renddesc.get_h();
520 surface->set_wh(w,h);
521
522 Point sample = tl, sub = tl, trans(0,0);
523 float xs = 0,ys = 0;
524 int y=0,x=0;
525 Real invpw = 1/pw, invph = 1/ph;
526 Surface::pen p = surface->begin();
527
528 Point rtl = r.get_tl();
529
530 //warning("Spherize: About to transform");
531
532 for(y = 0; y < h; ++y, sample[1] += ph, p.inc_y())
533 {
534 sub = sample;
535 for(x = 0; x < w; ++x, sub[0] += pw, p.inc_x())
536 {
537 bool clipped;
538 trans=sphtrans(sub,center,radius,percent,type,clipped);
539 if(clip && clipped)
540 {
541 p.put_value(Color::alpha());
542 continue;
543 }
544
545 xs = (trans[0]-rtl[0])*invpw;
546 ys = (trans[1]-rtl[1])*invph;
547
548 if(!(xs >= 0 && xs < nw && ys >= 0 && ys < nh))
549 {
550 //warning("Spherize: we failed to account for %f,%f",xs,ys);
551 p.put_value(context.get_color(trans));//Color::alpha());
552 continue;
553 }
554
555 //sample at that pixel location based on the quality
556 if(quality <= 4) // cubic
557 p.put_value(background.cubic_sample(xs,ys));
558 else if(quality <= 5) // cosine
559 p.put_value(background.cosine_sample(xs,ys));
560 else if(quality <= 6) // linear
561 p.put_value(background.linear_sample(xs,ys));
562 else // nearest
563 p.put_value(background[round_to_int(ys)][round_to_int(xs)]);
564 }
565 p.dec_x(w);
566 }
567
568 return true;
569 }
570
571 ////////
572 bool
accelerated_cairorender(Context context,cairo_t * cr,int quality,const RendDesc & renddesc_,ProgressCallback * cb) const573 Layer_SphereDistort::accelerated_cairorender(Context context, cairo_t *cr, int quality, const RendDesc &renddesc_, ProgressCallback *cb)const
574 {
575 /* Things to consider:
576 1) Block expansion for distortion (ouch... quality level??)
577 2) Bounding box clipping
578 3) Super sampling for better visual quality (based on the quality level?)
579 4) Interpolation type for sampling (based on quality level?)
580
581 //things to defer until after
582 super sampling, non-linear interpolation
583 */
584
585 Vector center=param_center.get(Vector());
586 double radius=param_radius.get(double());
587 double percent=param_amount.get(double());
588 int type=param_type.get(int());
589 bool clip=param_clip.get(bool());
590
591 RendDesc renddesc(renddesc_);
592
593 // Untransform the render desc
594 if(!cairo_renddesc_untransform(cr, renddesc))
595 return false;
596
597 //bounding box reject
598 {
599 Rect sphr;
600
601 sphr.set_point(center[0]-radius,center[1]-radius);
602 sphr.expand(center[0]+radius,center[1]+radius);
603
604 //get the bounding box of the transform
605 Rect windr;
606
607 //and the bounding box of the rendering
608 windr.set_point(renddesc.get_tl()[0],renddesc.get_tl()[1]);
609 windr.expand(renddesc.get_br()[0],renddesc.get_br()[1]);
610
611 //test bounding boxes for collision
612 if( (type == TYPE_NORMAL && !intersect(sphr,windr)) ||
613 (type == TYPE_DISTH && (sphr.minx >= windr.maxx || windr.minx >= sphr.maxx)) ||
614 (type == TYPE_DISTV && (sphr.miny >= windr.maxy || windr.miny >= sphr.maxy)) )
615 {
616 if (clip)
617 {
618 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
619 cairo_paint(cr);
620 return true;
621 }
622 else
623 return context.accelerated_cairorender(cr,quality,renddesc,cb);
624 }
625 }
626
627 //Ok, so we overlap some... now expand the window for rendering
628 RendDesc r = renddesc;
629 cairo_surface_t* background, *result;
630 int nl=0,nt=0,nr=0,nb=0, nw=0,nh=0;
631
632 // grab the current renddesc interesting values
633 const Real pw = renddesc.get_pw(),ph = renddesc.get_ph();
634 const Point tl = renddesc.get_tl(), br = renddesc.get_br();
635 const int w = renddesc.get_w(), h = renddesc.get_h();
636
637 {
638 //must enlarge window by pixel coordinates so go!
639
640 //need to figure out closest and farthest point and distort THOSE
641
642 Point origin[4] = {tl,tl,br,br};
643 Vector v[4] = {Vector(0,br[1]-tl[1]),
644 Vector(br[0]-tl[0],0),
645 Vector(0,tl[1]-br[1]),
646 Vector(tl[0]-br[0],0)};
647
648 Point close(0,0);
649 Real t = 0;
650 Rect expandr(tl,br);
651
652 //expandr.set_point(tl[0],tl[1]);
653 //expandr.expand(br[0],br[1]);
654
655 for(int i=0; i<4; ++i)
656 {
657 Vector p_o = center-origin[i];
658
659 //project onto left line
660 t = (p_o*v[i])/v[i].mag_squared();
661
662 //clamp
663 if(t < 0) t = 0; if(t > 1) t = 1;
664
665 close = origin[i] + v[i]*t;
666
667 //now get transforms and expand the rectangle to accommodate
668 Point p = sphtrans(close,center,radius,percent,type);
669 expandr.expand(p[0],p[1]);
670 p = sphtrans(origin[i],center,radius,percent,type);
671 expandr.expand(p[0],p[1]);
672 p = sphtrans(origin[i]+v[i],center,radius,percent,type);
673 expandr.expand(p[0],p[1]);
674 }
675
676 //now that we have the bounding rectangle of ALL the pixels (should be...)
677 //order it so that it's in the same orientation as the tl,br pair
678
679 Point ntl(0,0),nbr(0,0);
680
681 //sort x
682 if(tl[0] < br[0])
683 {
684 ntl[0] = expandr.minx;
685 nbr[0] = expandr.maxx;
686 }
687 else
688 {
689 ntl[0] = expandr.maxx;
690 nbr[0] = expandr.minx;
691 }
692
693 //sort y
694 if(tl[1] < br[1])
695 {
696 ntl[1] = expandr.miny;
697 nbr[1] = expandr.maxy;
698 }
699 else
700 {
701 ntl[1] = expandr.maxy;
702 nbr[1] = expandr.miny;
703 }
704
705 //now expand the window as needed
706 Vector temp = ntl-tl;
707
708 //pixel offset
709 nl = (int)(temp[0]/pw)-1;
710 nt = (int)(temp[1]/ph)-1;
711
712 temp = nbr - br;
713 nr = (int)(temp[0]/pw)+1;
714 nb = (int)(temp[1]/ph)+1;
715
716 nw = renddesc.get_w() + nr - nl;
717 nh = renddesc.get_h() + nb - nt;
718
719 r.set_subwindow(nl,nt,nw,nh);
720 }
721 // New values for expanded
722 const double wpw =r.get_pw();
723 const double wph =r.get_ph();
724 const double wtlx=r.get_tl()[0];
725 const double wtly=r.get_tl()[1];
726
727 // now we know the sice of the needed background, create the cairo surface
728 background=cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, nw, nh);
729 result=cairo_surface_create_similar(cairo_get_target(cr), CAIRO_CONTENT_COLOR_ALPHA, w, h);
730 // render the background
731 cairo_t* subcr=cairo_create(background);
732 cairo_scale(subcr, 1/wpw, 1/wph);
733 cairo_translate(subcr, -wtlx, -wtly);
734 if(!context.accelerated_cairorender(subcr,quality,r,cb))
735 {
736 warning("Cairo SphereDistort: Layer below failed");
737 return false;
738 }
739 cairo_destroy(subcr);
740 //now distort and check to make sure we aren't overshooting our bounds here
741
742 Point sample = tl, sub = tl, trans(0,0);
743 float xs = 0,ys = 0;
744 int y=0,x=0;
745 Real invpw = 1/pw, invph = 1/ph;
746 Point rtl = r.get_tl();
747
748 CairoSurface cresult(result);
749 if(!cresult.map_cairo_image())
750 {
751 warning("Sphere Distort: map cairo surface failed");
752 return false;
753 }
754 CairoSurface cbackground(background);
755 if(!cbackground.map_cairo_image())
756 {
757 warning("Sphere Distort: map cairo surface failed");
758 return false;
759 }
760
761 for(y = 0; y < h; ++y, sample[1] += ph)
762 {
763 sub = sample;
764 for(x = 0; x < w; ++x, sub[0] += pw)
765 {
766 bool clipped;
767 trans=sphtrans(sub,center,radius,percent,type,clipped);
768 if(clip && clipped)
769 {
770 cresult[y][x]=CairoColor::alpha();
771 continue;
772 }
773
774 xs = (trans[0]-rtl[0])*invpw;
775 ys = (trans[1]-rtl[1])*invph;
776
777 if(!(xs >= 0 && xs < nw && ys >= 0 && ys < nh))
778 {
779 cresult[y][x]=context.get_cairocolor(trans).premult_alpha();
780 continue;
781 }
782
783 //sample at that pixel location based on the quality
784 if(quality <= 4) // cubic
785 cresult[y][x]=cbackground.cubic_sample_cooked(xs, ys).premult_alpha();
786 else if(quality <= 5) // cosine
787 cresult[y][x]=cbackground.cosine_sample_cooked(xs, ys).premult_alpha();
788 else if(quality <= 6) // linear
789 cresult[y][x]=cbackground.linear_sample_cooked(xs, ys).premult_alpha();
790 else // nearest
791 cresult[y][x]=cbackground[round_to_int(ys)][round_to_int(xs)].premult_alpha();
792 }
793 }
794 cresult.unmap_cairo_image();
795 cbackground.unmap_cairo_image();
796
797 cairo_surface_destroy(background);
798
799 cairo_save(cr);
800
801 cairo_translate(cr, tl[0], tl[1]);
802 cairo_scale(cr, pw, ph);
803 cairo_set_source_surface(cr, result, 0, 0);
804 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
805 cairo_paint(cr);
806
807 cairo_restore(cr);
808 cairo_surface_destroy(result);
809
810 return true;
811 }
812
813 #endif
814
815 class lyr_std::Spherize_Trans : public Transform
816 {
817 etl::handle<const Layer_SphereDistort> layer;
818 public:
Spherize_Trans(const Layer_SphereDistort * x)819 Spherize_Trans(const Layer_SphereDistort* x):Transform(x->get_guid()),layer(x) { }
820
perform(const Vector & x) const821 Vector perform(const Vector& x)const
822 {
823 return sphtrans(x,layer->param_center.get(Vector()),layer->param_radius.get(double()),-layer->param_amount.get(double()),layer->param_type.get(int()));
824 }
825
unperform(const Vector & x) const826 Vector unperform(const Vector& x)const
827 {
828 return sphtrans(x,layer->param_center.get(Vector()),layer->param_radius.get(double()),-layer->param_amount.get(double()),layer->param_type.get(int()));
829 }
830
get_string() const831 String get_string()const
832 {
833 return "spheredistort";
834 }
835 };
836
837 etl::handle<Transform>
get_transform() const838 Layer_SphereDistort::get_transform()const
839 {
840 return new Spherize_Trans(this);
841 }
842
843 Rect
get_bounding_rect() const844 Layer_SphereDistort::get_bounding_rect()const
845 {
846 Vector center=param_center.get(Vector());
847 double radius=param_radius.get(double());
848 int type=param_type.get(int());
849 bool clip=param_clip.get(bool());
850
851 Rect bounds(Rect::full_plane());
852
853 if (clip)
854 return bounds;
855
856 switch(type)
857 {
858 case TYPE_NORMAL:
859 bounds=Rect(center[0]+radius, center[1]+radius,
860 center[0]-radius, center[1]-radius);
861 break;
862 case TYPE_DISTH:
863 bounds = Rect::vertical_strip(center[0]-radius, center[0]+radius);
864 break;
865 case TYPE_DISTV:
866 bounds = Rect::horizontal_strip(center[1]-radius, center[1]+radius);
867 break;
868 default:
869 break;
870 }
871
872 return bounds;
873 }
874