Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / synfig_0_61_04 / synfig-core / src / modules / lyr_std / sphere_distort.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file sphere_distort.cpp
3 **      \brief Sphere Distort File
4 **
5 **      $Id: sphere_distort.cpp,v 1.2 2005/01/24 05:00:18 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "sphere_distort.h"
33 #include <synfig/string.h>
34 #include <synfig/time.h>
35 #include <synfig/context.h>
36 #include <synfig/paramdesc.h>
37 #include <synfig/renddesc.h>
38 #include <synfig/surface.h>
39 #include <synfig/value.h>
40 #include <synfig/valuenode.h>
41 #include <synfig/transform.h>
42
43 #include <synfig/curve_helper.h>
44
45 #endif
46
47 /* === U S I N G =========================================================== */
48
49 using namespace std;
50 using namespace etl;
51 using namespace synfig;
52
53 /* === M A C R O S ========================================================= */
54
55 #ifndef PI
56 const double PI = 3.14159265;
57 #endif
58
59 enum
60 {
61         TYPE_NORMAL = 0,
62         TYPE_DISTH = 1, //axe the horizontal axis
63         TYPE_DISTV = 2, //axe the vertical axis
64         N_TYPES
65 };
66
67 /* === G L O B A L S ======================================================= */
68
69 SYNFIG_LAYER_INIT(Layer_SphereDistort);
70 SYNFIG_LAYER_SET_NAME(Layer_SphereDistort,"spherize");
71 SYNFIG_LAYER_SET_LOCAL_NAME(Layer_SphereDistort,_("Spherize"));
72 SYNFIG_LAYER_SET_CATEGORY(Layer_SphereDistort,_("Distortions"));
73 SYNFIG_LAYER_SET_VERSION(Layer_SphereDistort,"0.2");
74 SYNFIG_LAYER_SET_CVS_ID(Layer_SphereDistort,"$Id: sphere_distort.cpp,v 1.2 2005/01/24 05:00:18 darco Exp $");
75
76 /* === P R O C E D U R E S ================================================= */
77
78 /* === M E T H O D S ======================================================= */
79
80 /* === E N T R Y P O I N T ================================================= */
81
82 Layer_SphereDistort::Layer_SphereDistort()
83 :Layer_Composite(1.0,Color::BLEND_STRAIGHT),
84 center(0,0),
85 radius(1),
86 percent(1.0),
87 type(TYPE_NORMAL),
88 clip(false)
89 {
90 }
91
92         
93 bool
94 Layer_SphereDistort::set_param(const String & param, const ValueBase &value)
95 {
96         IMPORT_PLUS(center,sync());
97         IMPORT_PLUS(radius,sync());
98         IMPORT(type);
99         IMPORT_AS(percent,"amount");
100         IMPORT(clip);
101
102         if(param=="percent")
103         {
104                 if(dynamic_param_list().count("percent"))
105                 {
106                         connect_dynamic_param("amount",dynamic_param_list().find("percent")->second);
107                         disconnect_dynamic_param("percent");
108                         synfig::warning("Layer_SphereDistort::::set_param(): Updated valuenode connection to use the new \"amount\" parameter.");
109                 }
110                 else
111                         synfig::warning("Layer_SphereDistort::::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
112         }
113         
114         return false;   
115 }
116
117 ValueBase
118 Layer_SphereDistort::get_param(const String &param)const
119 {
120         EXPORT(center);
121         EXPORT(radius);
122         EXPORT(type);
123         EXPORT_AS(percent,"amount");
124         EXPORT(clip);
125         
126         EXPORT_NAME();
127         EXPORT_VERSION();
128                 
129         return ValueBase();
130 }
131
132 void
133 Layer_SphereDistort::sync()
134 {
135 }
136
137 Layer::Vocab
138 Layer_SphereDistort::get_param_vocab()const
139 {
140         Layer::Vocab ret;
141         
142         ret.push_back(ParamDesc("center")
143                 .set_local_name(_("Position"))
144         );
145         
146         ret.push_back(ParamDesc("radius")
147                 .set_local_name(_("Radius"))
148                 .set_origin("center")
149                 .set_is_distance()
150         );
151         
152         ret.push_back(ParamDesc("amount")
153                 .set_local_name(_("Amount"))
154                 .set_is_distance(false)
155         );
156
157         ret.push_back(ParamDesc("clip")
158                 .set_local_name(_("Clip"))
159         );
160         
161         ret.push_back(ParamDesc("type")
162                 .set_local_name(_("Distort Type"))
163                 .set_description(_("The direction of the distortion"))
164                 .set_hint("enum")
165                 .add_enum_value(TYPE_NORMAL,"normal",_("Spherize"))
166                 .add_enum_value(TYPE_DISTH,"honly",_("Vertical Bar"))
167                 .add_enum_value(TYPE_DISTV,"vonly",_("Horizontal Bar"))
168         );
169         
170         return ret;
171 }
172
173 /*
174         Spherical Distortion: maps an image onto a ellipsoid of some sort
175         
176         so the image coordinate (i.e. distance away from the center) 
177         will determine how things get mapped
178         
179         so with the radius and position the mapping would go as follows
180         
181         r = (pos - center) / radius     clamped to [-1,1]
182         
183         if it's outside of that range then it's not distorted
184         but if it's inside of that range then it goes as follows
185         
186         angle = r * pi/2 (-pi/2,pi/2)
187         
188         newr = cos(angle)*radius
189         
190         the inverse of this is (which is actually what we'd be transforming it from
191
192         
193 */
194
195 inline float spherify(float f)
196 {
197         if(f > -1 && f < 1 && f!=0)
198                 return sinf(f*(PI/2));
199         else return f;
200 }
201
202 inline float unspherify(float f)
203 {
204         if(f > -1 && f < 1 && f!=0)
205                 return asin(f)/(PI/2);
206         else return f;
207 }
208
209 Point sphtrans(const Point &p, const Point &center, const float &radius, 
210                                                                                         const Real &percent, int type, bool& clipped)
211 {
212         const Vector v = (p - center) / radius;
213         
214         Point newp = p;
215         const float t = percent;
216         
217         clipped=false;
218         
219         if(type == TYPE_NORMAL)
220         {
221                 const float m = v.mag();
222                 float lerp(0);
223                 
224                 if(m <= -1 || m >= 1)
225                 {
226                         clipped=true;
227                         return newp;
228                 }else
229                 if(m==0)
230                         return newp;
231                 else
232                 if(t > 0)
233                 {
234                         lerp = (t*unspherify(m) + (1-t)*m);
235                 }else if(t < 0)
236                 {                       
237                         lerp = ((1+t)*m - t*spherify(m));
238                 }else lerp = m;
239                 
240                 const float d = lerp*radius;
241                 newp = center + v*(d/m);
242         }
243         
244         else if(type == TYPE_DISTH)
245         {
246                 float lerp(0);
247                 if(v[0] <= -1 || v[0] >= 1)
248                 {
249                         clipped=true;
250                         return newp;
251                 }else
252                 if(v[0]==0)
253                         return newp;
254                 else
255                 if(t > 0)
256                 {
257                         lerp = (t*unspherify(v[0]) + (1-t)*v[0]);
258                 }else if(t < 0)
259                 {
260                         lerp = ((1+t)*v[0] - t*spherify(v[0]));
261                 }else lerp = v[0];
262                 
263                 newp[0] = center[0] + lerp*radius;
264         }
265         
266         else if(type == TYPE_DISTV)
267         {
268                 float lerp(0);
269                 if(v[1] <= -1 || v[1] >= 1)
270                 {
271                         clipped=true;
272                         return newp;
273                 }
274                 else
275                 if(v[1]==0)
276                         return newp;
277                 else
278                 if(t > 0)
279                 {
280                         lerp = (t*unspherify(v[1]) + (1-t)*v[1]);
281                 }else if(t < 0)
282                 {
283                         lerp = ((1+t)*v[1] - t*spherify(v[1]));
284                 }else lerp = v[1];
285                 
286                 newp[1] = center[1] + lerp*radius;
287         }
288         
289         return newp;
290 }
291
292 inline Point sphtrans(const Point &p, const Point &center, const Real &radius, 
293                                                                                         const Real &percent, int type)
294 {
295         bool tmp;
296         return sphtrans(p, center, radius, percent, type, tmp);
297 }
298
299 synfig::Layer::Handle
300 Layer_SphereDistort::hit_check(synfig::Context context, const synfig::Point &pos)const
301 {
302         bool clipped;
303         Point point(sphtrans(pos,center,radius,percent,type,clipped));
304         if(clip && clipped)
305                 return 0;
306         return context.hit_check(point);
307 }
308
309 Color
310 Layer_SphereDistort::get_color(Context context, const Point &pos)const
311 {
312         bool clipped;
313         Point point(sphtrans(pos,center,radius,percent,type,clipped));
314         if(clip && clipped)
315                 return Color::alpha();
316         return context.get_color(point);
317 }
318
319 #if 1
320 bool Layer_SphereDistort::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
321 {
322         /*      Things to consider:
323                 1) Block expansion for distortion (ouch... quality level??)
324                 2) Bounding box clipping
325                 3) Super sampling for better visual quality (based on the quality level?)
326                 4) Interpolation type for sampling (based on quality level?)
327         
328                 //things to defer until after
329                 super sampling, non-linear interpolation
330         */
331         
332         //bounding box reject
333         {
334                 Rect    sphr;
335                 
336                 sphr.set_point(center[0]-radius,center[1]-radius);
337                 sphr.expand(center[0]+radius,center[1]+radius);
338
339                 //get the bounding box of the transform         
340                 Rect    windr;
341                 
342                 //and the bounding box of the rendering         
343                 windr.set_point(renddesc.get_tl()[0],renddesc.get_tl()[1]);
344                 windr.expand(renddesc.get_br()[0],renddesc.get_br()[1]);
345                 
346                 //test bounding boxes for collision
347                 if( (type == TYPE_NORMAL && !intersect(sphr,windr)) ||
348                         (type == TYPE_DISTH && (sphr.minx >= windr.maxx || windr.minx >= sphr.maxx)) ||
349                         (type == TYPE_DISTV && (sphr.miny >= windr.maxy || windr.miny >= sphr.maxy)) )
350                 {
351                         //synfig::warning("Spherize: Bounding box reject");
352                         return context.accelerated_render(surface,quality,renddesc,cb);
353                 }
354                 
355                 //synfig::warning("Spherize: Bounding box accept");
356         }
357         
358         //Ok, so we overlap some... now expand the window for rendering
359         RendDesc r = renddesc;
360         Surface background;
361         Real pw = renddesc.get_pw(),ph = renddesc.get_ph();
362         
363         int nl=0,nt=0,nr=0,nb=0, nw=0,nh=0;
364         Point tl = renddesc.get_tl(), br = renddesc.get_br();
365         
366         {               
367                 //must enlarge window by pixel coordinates so go!
368                 
369                 //need to figure out closest and farthest point and distort THOSE
370                 
371                 Point origin[4] = {tl,tl,br,br};
372                 Vector v[4] = {Vector(0,br[1]-tl[1]),
373                                            Vector(br[0]-tl[0],0),
374                                            Vector(0,tl[1]-br[1]),
375                                            Vector(tl[0]-br[0],0)};
376                 
377                 Point close(0,0);
378                 Real t = 0;
379                 Rect    expandr(tl,br);
380                                            
381                 //expandr.set_point(tl[0],tl[1]);
382                 //expandr.expand(br[0],br[1]);
383
384                 //synfig::warning("Spherize: Loop through lines and stuff");                                       
385                 for(int i=0; i<4; ++i)
386                 {
387                         //synfig::warning("Spherize:    %d", i);
388                         Vector p_o = center-origin[i];
389                         
390                         //project onto left line
391                         t = (p_o*v[i])/v[i].mag_squared();
392                         
393                         //clamp
394                         if(t < 0) t = 0; if(t > 1) t = 1;
395                         
396                         close = origin[i] + v[i]*t;
397                         
398                         //now get transforms and expand the rectangle to accomodate
399                         Point p = sphtrans(close,center,radius,percent,type);
400                         expandr.expand(p[0],p[1]);
401                         p = sphtrans(origin[i],center,radius,percent,type);
402                         expandr.expand(p[0],p[1]);
403                         p = sphtrans(origin[i]+v[i],center,radius,percent,type);
404                         expandr.expand(p[0],p[1]);
405                 }
406                 
407                 /*synfig::warning("Spherize: Bounding box (%f,%f)-(%f,%f)",
408                                                         expandr.minx,expandr.miny,expandr.maxx,expandr.maxy);*/
409                 
410                 //now that we have the bouding rectangle of ALL the pixels (should be...)
411                 //order it so that it's in the same orientation as the tl,br pair
412
413                 //synfig::warning("Spherize: Organize like tl,br");
414                 Point ntl(0,0),nbr(0,0);
415                 
416                 //sort x
417                 if(tl[0] < br[0])
418                 { 
419                         ntl[0] = expandr.minx;
420                         nbr[0] = expandr.maxx;
421                 }
422                 else
423                 {
424                         ntl[0] = expandr.maxx;
425                         nbr[0] = expandr.minx;                  
426                 }
427                 
428                 //sort y
429                 if(tl[1] < br[1])
430                 { 
431                         ntl[1] = expandr.miny;
432                         nbr[1] = expandr.maxy;
433                 }
434                 else
435                 {
436                         ntl[1] = expandr.maxy;
437                         nbr[1] = expandr.miny;                  
438                 }
439                 
440                 //now expand the window as needed
441                 Vector temp = ntl-tl;
442                 
443                 //pixel offset
444                 nl = (int)(temp[0]/pw)-1;
445                 nt = (int)(temp[1]/ph)-1;
446                 
447                 temp = nbr - br;
448                 nr = (int)(temp[0]/pw)+1;
449                 nb = (int)(temp[1]/ph)+1;
450                 
451                 nw = renddesc.get_w() + nr - nl;
452                 nh = renddesc.get_h() + nb - nt;
453                 
454                 //synfig::warning("Spherize: Setting subwindow (%d,%d) (%d,%d) (%d,%d)",nl,nt,nr,nb,nw,nh);             
455                 r.set_subwindow(nl,nt,nw,nh);
456                 
457                 /*r = renddesc;
458                 nw = r.get_w(), nh = r.get_h();
459                 nl = 0, nt = 0;*/
460         }
461         
462         //synfig::warning("Spherize: render background");
463         if(!context.accelerated_render(&background,quality,r,cb))
464         {
465                 synfig::warning("SphereDistort: Layer below failed");
466                 return false;
467         }
468         
469         //now distort and check to make sure we aren't overshooting our bounds here
470         int w = renddesc.get_w(), h = renddesc.get_h();
471         surface->set_wh(w,h);
472                 
473         Point sample = tl, sub = tl, trans(0,0);
474         float xs = 0,ys = 0;
475         int y=0,x=0;
476         Real invpw = 1/pw, invph = 1/ph;
477         Surface::pen    p = surface->begin();
478         
479         Point rtl = r.get_tl();
480         
481         //synfig::warning("Spherize: About to transform");
482         
483         for(y = 0; y < h; ++y, sample[1] += ph, p.inc_y())
484         {
485                 sub = sample;
486                 for(x = 0; x < w; ++x, sub[0] += pw, p.inc_x())
487                 {
488                         bool clipped;
489                         trans=sphtrans(sub,center,radius,percent,type,clipped);
490                         if(clip && clipped)
491                         {
492                                 p.put_value(Color::alpha());
493                                 continue;
494                         }
495
496                         xs = (trans[0]-rtl[0])*invpw;
497                         ys = (trans[1]-rtl[1])*invph;
498                         
499                         if(!(xs >= 0 && xs < nw && ys >= 0 && ys < nh))
500                         {
501                                 //synfig::warning("Spherize: we failed to account for %f,%f",xs,ys);
502                                 p.put_value(context.get_color(trans));//Color::alpha());
503                                 continue;
504                         }
505                         
506                         //sample at that pixel location based on the quality            
507                         if(quality <= 4) //cubic
508                         {
509                                 p.put_value(background.cubic_sample(xs,ys));
510                         }else if(quality <= 5) //cosine
511                         {
512                                 p.put_value(background.cosine_sample(xs,ys));
513                         }else if(quality <= 6) //linear
514                         {
515                                 p.put_value(background.linear_sample(xs,ys));
516                         }else //nearest
517                         {
518                                 p.put_value(background[round_to_int(ys)][round_to_int(xs)]);
519                         }
520                 }
521                 p.dec_x(w);
522         }
523         
524         return true;    
525 }
526 #endif
527
528 class synfig::Spherize_Trans : public synfig::Transform
529 {
530         etl::handle<const Layer_SphereDistort> layer;
531 public:
532         Spherize_Trans(const Layer_SphereDistort* x):Transform(x->get_guid()),layer(x) { }
533         
534         synfig::Vector perform(const synfig::Vector& x)const
535         {
536                 return sphtrans(x,layer->center,layer->radius,-layer->percent,layer->type);
537         }
538         
539         synfig::Vector unperform(const synfig::Vector& x)const
540         {
541                 return sphtrans(x,layer->center,layer->radius,layer->percent,layer->type);
542         }
543 };
544
545 etl::handle<Transform>
546 Layer_SphereDistort::get_transform()const
547 {
548         return new Spherize_Trans(this);
549 }
550
551 Rect
552 Layer_SphereDistort::get_bounding_rect()const
553 {
554         Rect bounds(Rect::full_plane());
555         switch(type)
556         {
557                 case TYPE_NORMAL:
558                         bounds=Rect(
559                                 center[0]+(radius),
560                                 center[1]+(radius),
561                                 center[0]-(radius),
562                                 center[1]-(radius)
563                         );
564                         break;
565                 case TYPE_DISTH:
566                         break;
567                 case TYPE_DISTV:
568                         break;
569                 default:
570                         break;
571         }
572         
573         return bounds;
574 }
575