+++ /dev/null
-/* === S Y N F I G ========================================================= */
-/*! \file curvewarp.cpp
-** \brief Implementation of the "Curve Warp" layer
-**
-** $Id$
-**
-** \legal
-** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
-** Copyright (c) 2007-2008 Chris Moore
-**
-** 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
-**
-** === N O T E S ===========================================================
-**
-** ========================================================================= */
-
-/* === H E A D E R S ======================================================= */
-
-#ifdef USING_PCH
-# include "pch.h"
-#else
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "curvewarp.h"
-
-#include <synfig/context.h>
-#include <synfig/paramdesc.h>
-#include <synfig/surface.h>
-#include <synfig/valuenode.h>
-#include <ETL/calculus>
-
-#endif
-
-/* === M A C R O S ========================================================= */
-
-#define FAKE_TANGENT_STEP 0.000001
-#define TOO_THIN 0.01
-
-/* === G L O B A L S ======================================================= */
-
-SYNFIG_LAYER_INIT(CurveWarp);
-SYNFIG_LAYER_SET_NAME(CurveWarp,"curve_warp");
-SYNFIG_LAYER_SET_LOCAL_NAME(CurveWarp,N_("Curve Warp"));
-SYNFIG_LAYER_SET_CATEGORY(CurveWarp,N_("Distortions"));
-SYNFIG_LAYER_SET_VERSION(CurveWarp,"0.0");
-SYNFIG_LAYER_SET_CVS_ID(CurveWarp,"$Id$");
-
-/* === P R O C E D U R E S ================================================= */
-
-inline float calculate_distance(const std::vector<synfig::BLinePoint>& bline)
-{
- std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
- std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
-
- float dist(0);
-
- if (bline.empty()) return dist;
-
- next=bline.begin();
- iter=next++;
-
- for(;next!=end;iter=next++)
- {
- // Setup the curve
- etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
- dist+=curve.length();
- }
-
- return dist;
-}
-
-std::vector<synfig::BLinePoint>::const_iterator
-find_closest_to_bline(bool fast, const std::vector<synfig::BLinePoint>& bline,const Point& p,float& t, float& len, bool& extreme)
-{
- std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
- std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
-
- ret=bline.end();
- float dist(100000000000.0);
- next=bline.begin();
- float best_pos(0), best_len(0);
- etl::hermite<Vector> best_curve;
- iter=next++;
- Point bp;
- float total_len(0);
- bool first = true, last = false;
- extreme = false;
-
- for(;next!=end;iter=next++)
- {
- // Setup the curve
- etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
- float thisdist(0);
- last = false;
-
- if (fast)
- {
-#define POINT_CHECK(x) bp=curve(x); thisdist=(bp-p).mag_squared(); if(thisdist<dist) { extreme = (first&&x<0.01); ret=iter; best_len = total_len; dist=thisdist; best_curve=curve; last=true; best_pos=x;}
- POINT_CHECK(0.0001); POINT_CHECK((1.0/6)); POINT_CHECK((2.0/6)); POINT_CHECK((3.0/6));
- POINT_CHECK((4.0/6)); POINT_CHECK((5.0/6)); POINT_CHECK(0.9999);
- }
- else
- {
- float pos = curve.find_closest(fast, p);
- thisdist=(curve(pos)-p).mag_squared();
- if(thisdist<dist)
- {
- extreme = (first && pos == 0);
- ret=iter;
- dist=thisdist;
- best_pos = pos;
- best_curve = curve;
- best_len = total_len;
- last = true;
- }
- }
- total_len += curve.length();
- first = false;
- }
-
- t = best_pos;
- if (fast)
- {
- len = best_len + best_curve.find_distance(0,best_curve.find_closest(fast, p));
- if (last && t > .99) extreme = true;
- }
- else
- {
- len = best_len + best_curve.find_distance(0,best_pos);
- if (last && t == 1) extreme = true;
- }
- return ret;
-}
-
-/* === M E T H O D S ======================================================= */
-
-inline void
-CurveWarp::sync()
-{
- curve_length_=calculate_distance(bline);
- perp_ = (end_point - start_point).perp().norm();
-}
-
-CurveWarp::CurveWarp():
- origin(0,0),
- perp_width(1),
- start_point(-2.5,-0.5),
- end_point(2.5,-0.3),
- fast(true)
-{
- bline.push_back(BLinePoint());
- bline.push_back(BLinePoint());
- bline[0].set_vertex(Point(-2.5,0));
- bline[1].set_vertex(Point( 2.5,0));
- bline[0].set_tangent(Point(1, 0.1));
- bline[1].set_tangent(Point(1, -0.1));
- bline[0].set_width(1.0f);
- bline[1].set_width(1.0f);
-
- sync();
-}
-
-inline Point
-CurveWarp::transform(const Point &point_, Real *dist, Real *along, int quality)const
-{
- Vector tangent;
- Vector diff;
- Point p1;
- Real thickness;
- bool edge_case = false;
- float len(0);
- bool extreme;
- float t;
-
- if(bline.size()==0)
- return Point();
- else if(bline.size()==1)
- {
- tangent=bline.front().get_tangent1();
- p1=bline.front().get_vertex();
- thickness=bline.front().get_width();
- t = 0.5;
- extreme = false;
- }
- else
- {
- Point point(point_-origin);
-
- std::vector<synfig::BLinePoint>::const_iterator iter,next;
-
- // Figure out the BLinePoint we will be using,
- next=find_closest_to_bline(fast,bline,point,t,len,extreme);
-
- iter=next++;
- if(next==bline.end()) next=bline.begin();
-
- // Setup the curve
- etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
-
- // Setup the derivative function
- etl::derivative<etl::hermite<Vector> > deriv(curve);
-
- int search_iterations(7);
-
- if(quality<=6)search_iterations=7;
- else if(quality<=7)search_iterations=6;
- else if(quality<=8)search_iterations=5;
- else search_iterations=4;
-
- // Figure out the closest point on the curve
- if (fast) t = curve.find_closest(fast, point,search_iterations);
-
- // Calculate our values
- p1=curve(t); // the closest point on the curve
- tangent=deriv(t); // the tangent at that point
-
- // if the point we're nearest to is at either end of the
- // bline, our distance from the curve is the distance from the
- // point on the curve. we need to know which side of the
- // curve we're on, so find the average of the two tangents at
- // this point
- if (t<0.00001 || t>0.99999)
- {
- bool zero_tangent = (tangent[0] == 0 && tangent[1] == 0);
-
- if (t<0.5)
- {
- if (iter->get_split_tangent_flag() || zero_tangent)
- {
- // fake the current tangent if we need to
- if (zero_tangent) tangent = curve(FAKE_TANGENT_STEP) - curve(0);
-
- // calculate the other tangent
- Vector other_tangent(iter->get_tangent1());
- if (other_tangent[0] == 0 && other_tangent[1] == 0)
- {
- // find the previous blinepoint
- std::vector<synfig::BLinePoint>::const_iterator prev;
- if (iter != bline.begin()) (prev = iter)--;
- else prev = iter;
-
- etl::hermite<Vector> other_curve(prev->get_vertex(), iter->get_vertex(), prev->get_tangent2(), iter->get_tangent1());
- other_tangent = other_curve(1) - other_curve(1-FAKE_TANGENT_STEP);
- }
-
- // normalise and sum the two tangents
- tangent=(other_tangent.norm()+tangent.norm());
- edge_case=true;
- }
- }
- else
- {
- if (next->get_split_tangent_flag() || zero_tangent)
- {
- // fake the current tangent if we need to
- if (zero_tangent) tangent = curve(1) - curve(1-FAKE_TANGENT_STEP);
-
- // calculate the other tangent
- Vector other_tangent(next->get_tangent2());
- if (other_tangent[0] == 0 && other_tangent[1] == 0)
- {
- // find the next blinepoint
- std::vector<synfig::BLinePoint>::const_iterator next2(next);
- if (++next2 == bline.end())
- next2 = next;
-
- etl::hermite<Vector> other_curve(next->get_vertex(), next2->get_vertex(), next->get_tangent2(), next2->get_tangent1());
- other_tangent = other_curve(FAKE_TANGENT_STEP) - other_curve(0);
- }
-
- // normalise and sum the two tangents
- tangent=(other_tangent.norm()+tangent.norm());
- edge_case=true;
- }
- }
- }
- tangent = tangent.norm();
-
- // the width of the bline at the closest point on the curve
- thickness=(next->get_width()-iter->get_width())*t+iter->get_width();
- }
-
- if (thickness < TOO_THIN && thickness > -TOO_THIN)
- {
- if (thickness > 0) thickness = TOO_THIN;
- else thickness = -TOO_THIN;
- }
-
- if (extreme)
- {
- Vector tangent;
-
- if (t < 0.5)
- {
- std::vector<synfig::BLinePoint>::const_iterator iter(bline.begin());
- tangent = iter->get_tangent1().norm();
- len = 0;
- }
- else
- {
- std::vector<synfig::BLinePoint>::const_iterator iter(--bline.end());
- tangent = iter->get_tangent2().norm();
- len = curve_length_;
- }
- len += (point_-origin - p1)*tangent;
- diff = tangent.perp();
- }
- else if (edge_case)
- {
- diff=(p1-(point_-origin));
- if(diff*tangent.perp()<0) diff=-diff;
- diff=diff.norm();
- }
- else
- diff=tangent.perp();
-
- // diff is a unit vector perpendicular to the bline
- const Real unscaled_distance((point_-origin - p1)*diff);
- if (dist) *dist = unscaled_distance;
- if (along) *along = len;
- return ((start_point + (end_point - start_point) * len / curve_length_) +
- perp_ * unscaled_distance/(thickness*perp_width));
-}
-
-synfig::Layer::Handle
-CurveWarp::hit_check(synfig::Context context, const synfig::Point &point)const
-{
- return context.hit_check(transform(point));
-}
-
-bool
-CurveWarp::set_param(const String & param, const ValueBase &value)
-{
- IMPORT(origin);
- IMPORT(start_point);
- IMPORT(end_point);
- IMPORT(fast);
- IMPORT(perp_width);
-
- if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST)
- {
- bline=value;
- sync();
-
- return true;
- }
-
- IMPORT_AS(origin,"offset");
-
- return false;
-}
-
-ValueBase
-CurveWarp::get_param(const String & param)const
-{
- EXPORT(origin);
- EXPORT(start_point);
- EXPORT(end_point);
- EXPORT(bline);
- EXPORT(fast);
- EXPORT(perp_width);
-
- EXPORT_NAME();
- EXPORT_VERSION();
-
- return ValueBase();
-}
-
-Layer::Vocab
-CurveWarp::get_param_vocab()const
-{
- Layer::Vocab ret;
-
- ret.push_back(ParamDesc("origin")
- .set_local_name(_("Origin")));
-
- ret.push_back(ParamDesc("perp_width")
- .set_local_name(_("Width"))
- .set_origin("start_point"));
-
- ret.push_back(ParamDesc("start_point")
- .set_local_name(_("Start Point"))
- .set_connect("end_point"));
-
- ret.push_back(ParamDesc("end_point")
- .set_local_name(_("End Point")));
-
- ret.push_back(ParamDesc("bline")
- .set_local_name(_("Vertices"))
- .set_origin("origin")
- .set_hint("width")
- .set_description(_("A list of BLine Points")));
-
- ret.push_back(ParamDesc("fast")
- .set_local_name(_("Fast")));
-
- return ret;
-}
-
-Color
-CurveWarp::get_color(Context context, const Point &point)const
-{
- return context.get_color(transform(point));
-}
-
-bool
-CurveWarp::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
-{
- SuperCallback stageone(cb,0,9000,10000);
- SuperCallback stagetwo(cb,9000,10000,10000);
-
- int x,y;
-
- const Real pw(renddesc.get_pw()),ph(renddesc.get_ph());
- Point tl(renddesc.get_tl());
- Point br(renddesc.get_br());
- const int w(renddesc.get_w());
- const int h(renddesc.get_h());
-
- // find a bounding rectangle for the context we need to render
- // todo: find a better way of doing this - this way doesn't work
- Rect src_rect(transform(tl));
- Point pos1, pos2;
- Real dist, along;
- Real min_dist(999999), max_dist(-999999), min_along(999999), max_along(-999999);
-
-#define UPDATE_DIST \
- if (dist < min_dist) min_dist = dist; \
- if (dist > max_dist) max_dist = dist; \
- if (along < min_along) min_along = along; \
- if (along > max_along) max_along = along
-
- // look along the top and bottom edges
- pos1[0] = pos2[0] = tl[0]; pos1[1] = tl[1]; pos2[1] = br[1];
- for (x = 0; x < w; x++, pos1[0] += pw, pos2[0] += pw)
- {
- src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST;
- src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST;
- }
-
- // look along the left and right edges
- pos1[0] = tl[0]; pos2[0] = br[0]; pos1[1] = pos2[1] = tl[1];
- for (y = 0; y < h; y++, pos1[1] += ph, pos2[1] += ph)
- {
- src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST;
- src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST;
- }
-
- // look along the diagonals
- const int max_wh(std::max(w,h));
- const Real inc_x((br[0]-tl[0])/max_wh),inc_y((br[1]-tl[1])/max_wh);
- pos1[0] = pos2[0] = tl[0]; pos1[1] = tl[1]; pos2[1] = br[1];
- for (x = 0; x < max_wh; x++, pos1[0] += inc_x, pos2[0] = pos1[0], pos1[1]+=inc_y, pos2[1]-=inc_y)
- {
- src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST;
- src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST;
- }
-
-#if 0
- // look at each blinepoint
- std::vector<synfig::BLinePoint>::const_iterator iter;
- for (iter=bline.begin(); iter!=bline.end(); iter++)
- src_rect.expand(transform(iter->get_vertex()+origin, &dist, &along)); UPDATE_DIST;
-#endif
-
- Point src_tl(src_rect.get_min());
- Point src_br(src_rect.get_max());
-
- Vector ab((end_point - start_point).norm());
- Angle::tan ab_angle(ab[1], ab[0]);
-
- Real used_length = max_along - min_along;
- Real render_width = max_dist - min_dist;
-
- int src_w = (abs(used_length*Angle::cos(ab_angle).get()) +
- abs(render_width*Angle::sin(ab_angle).get())) / abs(pw);
- int src_h = (abs(used_length*Angle::sin(ab_angle).get()) +
- abs(render_width*Angle::cos(ab_angle).get())) / abs(ph);
-
- Real src_pw((src_br[0] - src_tl[0]) / src_w);
- Real src_ph((src_br[1] - src_tl[1]) / src_h);
-
- if (src_pw > abs(pw))
- {
- src_w = int((src_br[0] - src_tl[0]) / abs(pw));
- src_pw = (src_br[0] - src_tl[0]) / src_w;
- }
-
- if (src_ph > abs(ph))
- {
- src_h = int((src_br[1] - src_tl[1]) / abs(ph));
- src_ph = (src_br[1] - src_tl[1]) / src_h;
- }
-
-#define MAXPIX 10000
- if (src_w > MAXPIX) src_w = MAXPIX;
- if (src_h > MAXPIX) src_h = MAXPIX;
-
- // this is an attempt to remove artifacts around tile edges - the
- // cubic interpolation uses at most 2 pixels either side of the
- // target pixel, so add an extra 2 pixels around the tile on all
- // sides
- src_tl -= (Point(src_pw,src_ph)*2);
- src_br += (Point(src_pw,src_ph)*2);
- src_w += 4;
- src_h += 4;
- src_pw = (src_br[0] - src_tl[0]) / src_w;
- src_ph = (src_br[1] - src_tl[1]) / src_h;
-
- // set up a renddesc for the context to render
- RendDesc src_desc(renddesc);
- src_desc.clear_flags();
- src_desc.set_tl(src_tl);
- src_desc.set_br(src_br);
- src_desc.set_wh(src_w, src_h);
-
- // render the context onto a new surface
- Surface source;
- source.set_wh(src_w,src_h);
- if(!context.accelerated_render(&source,quality,src_desc,&stageone))
- return false;
-
- float u,v;
- Point pos, tmp;
-
- surface->set_wh(w,h);
- surface->clear();
-
- if(quality<=4) // CUBIC
- for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
- {
- for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
- {
- tmp=transform(pos);
- u=(tmp[0]-src_tl[0])/src_pw;
- v=(tmp[1]-src_tl[1])/src_ph;
- if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v))
- (*surface)[y][x]=context.get_color(tmp);
- else
- (*surface)[y][x]=source.cubic_sample(u,v);
- }
- if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false;
- }
- else if (quality<=6) // INTERPOLATION_LINEAR
- for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
- {
- for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
- {
- tmp=transform(pos);
- u=(tmp[0]-src_tl[0])/src_pw;
- v=(tmp[1]-src_tl[1])/src_ph;
- if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v))
- (*surface)[y][x]=context.get_color(tmp);
- else
- (*surface)[y][x]=source.linear_sample(u,v);
- }
- if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false;
- }
- else // NEAREST_NEIGHBOR
- for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
- {
- for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
- {
- tmp=transform(pos);
- u=(tmp[0]-src_tl[0])/src_pw;
- v=(tmp[1]-src_tl[1])/src_ph;
- if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v))
- (*surface)[y][x]=context.get_color(tmp);
- else
- (*surface)[y][x]=source[floor_to_int(v)][floor_to_int(u)];
- }
- if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false;
- }
-
- // Mark our progress as finished
- if(cb && !cb->amount_complete(10000,10000))
- return false;
-
- return true;
-}