-/* === S Y N F I G ========================================================= */
-/*! \file sphere_distort.cpp
-** \brief Sphere Distort File
-**
-** $Id: sphere_distort.cpp,v 1.2 2005/01/24 05:00:18 darco Exp $
-**
-** \legal
-** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
-**
-** This package is free software; you can redistribute it and/or
-** modify it under the terms of the GNU General Public License as
-** published by the Free Software Foundation; either version 2 of
-** the License, or (at your option) any later version.
-**
-** This package is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-** General Public License for more details.
-** \endlegal
-*/
-/* ========================================================================= */
-
-/* === H E A D E R S ======================================================= */
-
-#ifdef USING_PCH
-# include "pch.h"
-#else
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "sphere_distort.h"
-#include <synfig/string.h>
-#include <synfig/time.h>
-#include <synfig/context.h>
-#include <synfig/paramdesc.h>
-#include <synfig/renddesc.h>
-#include <synfig/surface.h>
-#include <synfig/value.h>
-#include <synfig/valuenode.h>
-#include <synfig/transform.h>
-
-#include <synfig/curve_helper.h>
-
-#endif
-
-/* === U S I N G =========================================================== */
-
-using namespace std;
-using namespace etl;
-using namespace synfig;
-
-/* === M A C R O S ========================================================= */
-
-#ifndef PI
-const double PI = 3.14159265;
-#endif
-
-enum
-{
- TYPE_NORMAL = 0,
- TYPE_DISTH = 1, //axe the horizontal axis
- TYPE_DISTV = 2, //axe the vertical axis
- N_TYPES
-};
-
-/* === G L O B A L S ======================================================= */
-
-SYNFIG_LAYER_INIT(Layer_SphereDistort);
-SYNFIG_LAYER_SET_NAME(Layer_SphereDistort,"spherize");
-SYNFIG_LAYER_SET_LOCAL_NAME(Layer_SphereDistort,_("Spherize"));
-SYNFIG_LAYER_SET_CATEGORY(Layer_SphereDistort,_("Distortions"));
-SYNFIG_LAYER_SET_VERSION(Layer_SphereDistort,"0.2");
-SYNFIG_LAYER_SET_CVS_ID(Layer_SphereDistort,"$Id: sphere_distort.cpp,v 1.2 2005/01/24 05:00:18 darco Exp $");
-
-/* === P R O C E D U R E S ================================================= */
-
-/* === M E T H O D S ======================================================= */
-
-/* === E N T R Y P O I N T ================================================= */
-
-Layer_SphereDistort::Layer_SphereDistort()
-:Layer_Composite(1.0,Color::BLEND_STRAIGHT),
-center(0,0),
-radius(1),
-percent(1.0),
-type(TYPE_NORMAL),
-clip(false)
-{
-}
-
-
-bool
-Layer_SphereDistort::set_param(const String & param, const ValueBase &value)
-{
- IMPORT_PLUS(center,sync());
- IMPORT_PLUS(radius,sync());
- IMPORT(type);
- IMPORT_AS(percent,"amount");
- IMPORT(clip);
-
- if(param=="percent")
- {
- if(dynamic_param_list().count("percent"))
- {
- connect_dynamic_param("amount",dynamic_param_list().find("percent")->second);
- disconnect_dynamic_param("percent");
- synfig::warning("Layer_SphereDistort::::set_param(): Updated valuenode connection to use the new \"amount\" parameter.");
- }
- else
- synfig::warning("Layer_SphereDistort::::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
- }
-
- return false;
-}
-
-ValueBase
-Layer_SphereDistort::get_param(const String ¶m)const
-{
- EXPORT(center);
- EXPORT(radius);
- EXPORT(type);
- EXPORT_AS(percent,"amount");
- EXPORT(clip);
-
- EXPORT_NAME();
- EXPORT_VERSION();
-
- return ValueBase();
-}
-
-void
-Layer_SphereDistort::sync()
-{
-}
-
-Layer::Vocab
-Layer_SphereDistort::get_param_vocab()const
-{
- Layer::Vocab ret;
-
- ret.push_back(ParamDesc("center")
- .set_local_name(_("Position"))
- );
-
- ret.push_back(ParamDesc("radius")
- .set_local_name(_("Radius"))
- .set_origin("center")
- .set_is_distance()
- );
-
- ret.push_back(ParamDesc("amount")
- .set_local_name(_("Amount"))
- .set_is_distance(false)
- );
-
- ret.push_back(ParamDesc("clip")
- .set_local_name(_("Clip"))
- );
-
- ret.push_back(ParamDesc("type")
- .set_local_name(_("Distort Type"))
- .set_description(_("The direction of the distortion"))
- .set_hint("enum")
- .add_enum_value(TYPE_NORMAL,"normal",_("Spherize"))
- .add_enum_value(TYPE_DISTH,"honly",_("Vertical Bar"))
- .add_enum_value(TYPE_DISTV,"vonly",_("Horizontal Bar"))
- );
-
- return ret;
-}
-
-/*
- Spherical Distortion: maps an image onto a ellipsoid of some sort
-
- so the image coordinate (i.e. distance away from the center)
- will determine how things get mapped
-
- so with the radius and position the mapping would go as follows
-
- r = (pos - center) / radius clamped to [-1,1]
-
- if it's outside of that range then it's not distorted
- but if it's inside of that range then it goes as follows
-
- angle = r * pi/2 (-pi/2,pi/2)
-
- newr = cos(angle)*radius
-
- the inverse of this is (which is actually what we'd be transforming it from
-
-
-*/
-
-inline float spherify(float f)
-{
- if(f > -1 && f < 1 && f!=0)
- return sinf(f*(PI/2));
- else return f;
-}
-
-inline float unspherify(float f)
-{
- if(f > -1 && f < 1 && f!=0)
- return asin(f)/(PI/2);
- else return f;
-}
-
-Point sphtrans(const Point &p, const Point ¢er, const float &radius,
- const Real &percent, int type, bool& clipped)
-{
- const Vector v = (p - center) / radius;
-
- Point newp = p;
- const float t = percent;
-
- clipped=false;
-
- if(type == TYPE_NORMAL)
- {
- const float m = v.mag();
- float lerp(0);
-
- if(m <= -1 || m >= 1)
- {
- clipped=true;
- return newp;
- }else
- if(m==0)
- return newp;
- else
- if(t > 0)
- {
- lerp = (t*unspherify(m) + (1-t)*m);
- }else if(t < 0)
- {
- lerp = ((1+t)*m - t*spherify(m));
- }else lerp = m;
-
- const float d = lerp*radius;
- newp = center + v*(d/m);
- }
-
- else if(type == TYPE_DISTH)
- {
- float lerp(0);
- if(v[0] <= -1 || v[0] >= 1)
- {
- clipped=true;
- return newp;
- }else
- if(v[0]==0)
- return newp;
- else
- if(t > 0)
- {
- lerp = (t*unspherify(v[0]) + (1-t)*v[0]);
- }else if(t < 0)
- {
- lerp = ((1+t)*v[0] - t*spherify(v[0]));
- }else lerp = v[0];
-
- newp[0] = center[0] + lerp*radius;
- }
-
- else if(type == TYPE_DISTV)
- {
- float lerp(0);
- if(v[1] <= -1 || v[1] >= 1)
- {
- clipped=true;
- return newp;
- }
- else
- if(v[1]==0)
- return newp;
- else
- if(t > 0)
- {
- lerp = (t*unspherify(v[1]) + (1-t)*v[1]);
- }else if(t < 0)
- {
- lerp = ((1+t)*v[1] - t*spherify(v[1]));
- }else lerp = v[1];
-
- newp[1] = center[1] + lerp*radius;
- }
-
- return newp;
-}
-
-inline Point sphtrans(const Point &p, const Point ¢er, const Real &radius,
- const Real &percent, int type)
-{
- bool tmp;
- return sphtrans(p, center, radius, percent, type, tmp);
-}
-
-synfig::Layer::Handle
-Layer_SphereDistort::hit_check(synfig::Context context, const synfig::Point &pos)const
-{
- bool clipped;
- Point point(sphtrans(pos,center,radius,percent,type,clipped));
- if(clip && clipped)
- return 0;
- return context.hit_check(point);
-}
-
-Color
-Layer_SphereDistort::get_color(Context context, const Point &pos)const
-{
- bool clipped;
- Point point(sphtrans(pos,center,radius,percent,type,clipped));
- if(clip && clipped)
- return Color::alpha();
- return context.get_color(point);
-}
-
-#if 1
-bool Layer_SphereDistort::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
-{
- /* Things to consider:
- 1) Block expansion for distortion (ouch... quality level??)
- 2) Bounding box clipping
- 3) Super sampling for better visual quality (based on the quality level?)
- 4) Interpolation type for sampling (based on quality level?)
-
- //things to defer until after
- super sampling, non-linear interpolation
- */
-
- //bounding box reject
- {
- Rect sphr;
-
- sphr.set_point(center[0]-radius,center[1]-radius);
- sphr.expand(center[0]+radius,center[1]+radius);
-
- //get the bounding box of the transform
- Rect windr;
-
- //and the bounding box of the rendering
- windr.set_point(renddesc.get_tl()[0],renddesc.get_tl()[1]);
- windr.expand(renddesc.get_br()[0],renddesc.get_br()[1]);
-
- //test bounding boxes for collision
- if( (type == TYPE_NORMAL && !intersect(sphr,windr)) ||
- (type == TYPE_DISTH && (sphr.minx >= windr.maxx || windr.minx >= sphr.maxx)) ||
- (type == TYPE_DISTV && (sphr.miny >= windr.maxy || windr.miny >= sphr.maxy)) )
- {
- //synfig::warning("Spherize: Bounding box reject");
- return context.accelerated_render(surface,quality,renddesc,cb);
- }
-
- //synfig::warning("Spherize: Bounding box accept");
- }
-
- //Ok, so we overlap some... now expand the window for rendering
- RendDesc r = renddesc;
- Surface background;
- Real pw = renddesc.get_pw(),ph = renddesc.get_ph();
-
- int nl=0,nt=0,nr=0,nb=0, nw=0,nh=0;
- Point tl = renddesc.get_tl(), br = renddesc.get_br();
-
- {
- //must enlarge window by pixel coordinates so go!
-
- //need to figure out closest and farthest point and distort THOSE
-
- Point origin[4] = {tl,tl,br,br};
- Vector v[4] = {Vector(0,br[1]-tl[1]),
- Vector(br[0]-tl[0],0),
- Vector(0,tl[1]-br[1]),
- Vector(tl[0]-br[0],0)};
-
- Point close(0,0);
- Real t = 0;
- Rect expandr(tl,br);
-
- //expandr.set_point(tl[0],tl[1]);
- //expandr.expand(br[0],br[1]);
-
- //synfig::warning("Spherize: Loop through lines and stuff");
- for(int i=0; i<4; ++i)
- {
- //synfig::warning("Spherize: %d", i);
- Vector p_o = center-origin[i];
-
- //project onto left line
- t = (p_o*v[i])/v[i].mag_squared();
-
- //clamp
- if(t < 0) t = 0; if(t > 1) t = 1;
-
- close = origin[i] + v[i]*t;
-
- //now get transforms and expand the rectangle to accomodate
- Point p = sphtrans(close,center,radius,percent,type);
- expandr.expand(p[0],p[1]);
- p = sphtrans(origin[i],center,radius,percent,type);
- expandr.expand(p[0],p[1]);
- p = sphtrans(origin[i]+v[i],center,radius,percent,type);
- expandr.expand(p[0],p[1]);
- }
-
- /*synfig::warning("Spherize: Bounding box (%f,%f)-(%f,%f)",
- expandr.minx,expandr.miny,expandr.maxx,expandr.maxy);*/
-
- //now that we have the bouding rectangle of ALL the pixels (should be...)
- //order it so that it's in the same orientation as the tl,br pair
-
- //synfig::warning("Spherize: Organize like tl,br");
- Point ntl(0,0),nbr(0,0);
-
- //sort x
- if(tl[0] < br[0])
- {
- ntl[0] = expandr.minx;
- nbr[0] = expandr.maxx;
- }
- else
- {
- ntl[0] = expandr.maxx;
- nbr[0] = expandr.minx;
- }
-
- //sort y
- if(tl[1] < br[1])
- {
- ntl[1] = expandr.miny;
- nbr[1] = expandr.maxy;
- }
- else
- {
- ntl[1] = expandr.maxy;
- nbr[1] = expandr.miny;
- }
-
- //now expand the window as needed
- Vector temp = ntl-tl;
-
- //pixel offset
- nl = (int)(temp[0]/pw)-1;
- nt = (int)(temp[1]/ph)-1;
-
- temp = nbr - br;
- nr = (int)(temp[0]/pw)+1;
- nb = (int)(temp[1]/ph)+1;
-
- nw = renddesc.get_w() + nr - nl;
- nh = renddesc.get_h() + nb - nt;
-
- //synfig::warning("Spherize: Setting subwindow (%d,%d) (%d,%d) (%d,%d)",nl,nt,nr,nb,nw,nh);
- r.set_subwindow(nl,nt,nw,nh);
-
- /*r = renddesc;
- nw = r.get_w(), nh = r.get_h();
- nl = 0, nt = 0;*/
- }
-
- //synfig::warning("Spherize: render background");
- if(!context.accelerated_render(&background,quality,r,cb))
- {
- synfig::warning("SphereDistort: Layer below failed");
- return false;
- }
-
- //now distort and check to make sure we aren't overshooting our bounds here
- int w = renddesc.get_w(), h = renddesc.get_h();
- surface->set_wh(w,h);
-
- Point sample = tl, sub = tl, trans(0,0);
- float xs = 0,ys = 0;
- int y=0,x=0;
- Real invpw = 1/pw, invph = 1/ph;
- Surface::pen p = surface->begin();
-
- Point rtl = r.get_tl();
-
- //synfig::warning("Spherize: About to transform");
-
- for(y = 0; y < h; ++y, sample[1] += ph, p.inc_y())
- {
- sub = sample;
- for(x = 0; x < w; ++x, sub[0] += pw, p.inc_x())
- {
- bool clipped;
- trans=sphtrans(sub,center,radius,percent,type,clipped);
- if(clip && clipped)
- {
- p.put_value(Color::alpha());
- continue;
- }
-
- xs = (trans[0]-rtl[0])*invpw;
- ys = (trans[1]-rtl[1])*invph;
-
- if(!(xs >= 0 && xs < nw && ys >= 0 && ys < nh))
- {
- //synfig::warning("Spherize: we failed to account for %f,%f",xs,ys);
- p.put_value(context.get_color(trans));//Color::alpha());
- continue;
- }
-
- //sample at that pixel location based on the quality
- if(quality <= 4) //cubic
- {
- p.put_value(background.cubic_sample(xs,ys));
- }else if(quality <= 5) //cosine
- {
- p.put_value(background.cosine_sample(xs,ys));
- }else if(quality <= 6) //linear
- {
- p.put_value(background.linear_sample(xs,ys));
- }else //nearest
- {
- p.put_value(background[round_to_int(ys)][round_to_int(xs)]);
- }
- }
- p.dec_x(w);
- }
-
- return true;
-}
-#endif
-
-class synfig::Spherize_Trans : public synfig::Transform
-{
- etl::handle<const Layer_SphereDistort> layer;
-public:
- Spherize_Trans(const Layer_SphereDistort* x):Transform(x->get_guid()),layer(x) { }
-
- synfig::Vector perform(const synfig::Vector& x)const
- {
- return sphtrans(x,layer->center,layer->radius,-layer->percent,layer->type);
- }
-
- synfig::Vector unperform(const synfig::Vector& x)const
- {
- return sphtrans(x,layer->center,layer->radius,layer->percent,layer->type);
- }
-};
-
-etl::handle<Transform>
-Layer_SphereDistort::get_transform()const
-{
- return new Spherize_Trans(this);
-}
-
-Rect
-Layer_SphereDistort::get_bounding_rect()const
-{
- Rect bounds(Rect::full_plane());
- switch(type)
- {
- case TYPE_NORMAL:
- bounds=Rect(
- center[0]+(radius),
- center[1]+(radius),
- center[0]-(radius),
- center[1]-(radius)
- );
- break;
- case TYPE_DISTH:
- break;
- case TYPE_DISTV:
- break;
- default:
- break;
- }
-
- return bounds;
-}
-