1 /* === S Y N F I G ========================================================= */
3 ** \brief Implementation of the "Warp" layer
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007, 2008 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 ** === N O T E S ===========================================================
24 ** ========================================================================= */
26 /* === H E A D E R S ======================================================= */
36 #include <synfig/string.h>
37 #include <synfig/time.h>
38 #include <synfig/context.h>
39 #include <synfig/paramdesc.h>
40 #include <synfig/renddesc.h>
41 #include <synfig/surface.h>
42 #include <synfig/value.h>
43 #include <synfig/valuenode.h>
44 #include <synfig/transform.h>
49 /* === M A C R O S ========================================================= */
51 /* === G L O B A L S ======================================================= */
53 SYNFIG_LAYER_INIT(Warp);
54 SYNFIG_LAYER_SET_NAME(Warp,"warp");
55 SYNFIG_LAYER_SET_LOCAL_NAME(Warp,N_("Warp"));
56 SYNFIG_LAYER_SET_CATEGORY(Warp,N_("Distortions"));
57 SYNFIG_LAYER_SET_VERSION(Warp,"0.1");
58 SYNFIG_LAYER_SET_CVS_ID(Warp,"$Id$");
60 /* === P R O C E D U R E S ================================================= */
62 /* === M E T H O D S ======================================================= */
64 /* === E N T R Y P O I N T ================================================= */
77 Layer::Vocab voc(get_param_vocab());
78 Layer::fill_static(voc);
86 Warp::transform_forward(const Point& p)const
89 (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]),
90 (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])
95 Warp::transform_backward(const Point& p)const
98 (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]),
99 (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])
104 Warp::transform_forward_z(const Point& p)const
106 return inv_matrix[2][0]*p[0] + inv_matrix[2][1]*p[1] + inv_matrix[2][2];
110 Warp::transform_backward_z(const Point& p)const
112 return matrix[2][0]*p[0] + matrix[2][1]*p[1] + matrix[2][2];
116 #define transform_forward(p) Point( \
117 cache_a*p[0] + cache_b*p[1] + cache_c*p[0]*p[1] + cache_d, \
118 cache_e*p[0] + cache_f*p[1] + cache_i*p[0]*p[1] + cache_j )
120 #define transform_backward(p) Point( \
121 cache_a*p[0] + cache_b*p[1] + cache_c*p[0]*p[1] + cache_d, \
122 cache_e*p[0] + cache_f*p[1] + cache_i*p[0]*p[1] + cache_j )
125 #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]))
126 #define quad_area(a,b,c,d) (triangle_area(a,b,c)+triangle_area(a,c,d))
128 Real mat3_determinant(Real matrix[3][3])
132 ret = (matrix[0][0] *
133 (matrix[1][1] * matrix[2][2] -
134 matrix[1][2] * matrix[2][1]));
135 ret -= (matrix[1][0] *
136 (matrix[0][1] * matrix[2][2] -
137 matrix[0][2] * matrix[2][1]));
138 ret += (matrix[2][0] *
139 (matrix[0][1] * matrix[1][2] -
140 matrix[0][2] * matrix[1][1]));
145 void mat3_invert(Real in[3][3], Real out[3][3])
147 Real det(mat3_determinant(in));
154 out[0][0] = (in[1][1] * in[2][2] -
155 in[1][2] * in[2][1]) * det;
157 out[1][0] = - (in[1][0] * in[2][2] -
158 in[1][2] * in[2][0]) * det;
160 out[2][0] = (in[1][0] * in[2][1] -
161 in[1][1] * in[2][0]) * det;
163 out[0][1] = - (in[0][1] * in[2][2] -
164 in[0][2] * in[2][1]) * det;
166 out[1][1] = (in[0][0] * in[2][2] -
167 in[0][2] * in[2][0]) * det;
169 out[2][1] = - (in[0][0] * in[2][1] -
170 in[0][1] * in[2][0]) * det;
172 out[0][2] = (in[0][1] * in[1][2] -
173 in[0][2] * in[1][1]) * det;
175 out[1][2] = - (in[0][0] * in[1][2] -
176 in[0][2] * in[1][0]) * det;
178 out[2][2] = (in[0][0] * in[1][1] -
179 in[0][1] * in[1][0]) * det;
186 /* cache_a=(-dest_tl[0]+dest_tr[0])/(src_br[1]-src_tl[1]);
187 cache_b=(-dest_tl[0]+dest_bl[0])/(src_br[0]-src_tl[0]);
188 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]));
191 cache_e=(-dest_tl[1]+dest_tr[1])/(src_br[0]-src_tl[0]);
192 cache_f=(-dest_tl[1]+dest_bl[1])/(src_br[1]-src_tl[1]);
193 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]));
197 /* 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]));
198 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]));
199 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]));
201 matrix[0][0]=-(-dest_tl[1]+dest_tr[1])/(src_br[0]-src_tl[0]);
202 matrix[0][1]=-(-dest_tl[1]+dest_bl[1])/(src_br[1]-src_tl[1]);
204 matrix[1][0]=-(-dest_tl[0]+dest_tr[0])/(src_br[1]-src_tl[1]);
205 matrix[1][1]=-(-dest_tl[0]+dest_bl[0])/(src_br[0]-src_tl[0]);
207 matrix[0][2]=matrix[0][0]*dest_tl[0] + matrix[0][1]*dest_tl[1];
208 matrix[1][2]=matrix[1][0]*dest_tl[0] + matrix[1][1]*dest_tl[1];
214 const Real& x1(min(src_br[0],src_tl[0]));
215 const Real& y1(min(src_br[1],src_tl[1]));
216 const Real& x2(max(src_br[0],src_tl[0]));
217 const Real& y2(max(src_br[1],src_tl[1]));
219 Real tx1(dest_bl[0]);
220 Real ty1(dest_bl[1]);
221 Real tx2(dest_br[0]);
222 Real ty2(dest_br[1]);
223 Real tx3(dest_tl[0]);
224 Real ty3(dest_tl[1]);
225 Real tx4(dest_tr[0]);
226 Real ty4(dest_tr[1]);
228 if(src_br[0]<src_tl[0])
229 swap(tx3,tx4),swap(ty3,ty4),swap(tx1,tx2),swap(ty1,ty2);
231 if(src_br[1]>src_tl[1])
232 swap(tx3,tx1),swap(ty3,ty1),swap(tx4,tx2),swap(ty4,ty2);
237 scalex = scaley = 1.0;
240 scalex = 1.0 / (Real) (x2 - x1);
243 scaley = 1.0 / (Real) (y2 - y1);
245 /* Determine the perspective transform that maps from
246 * the unit cube to the transformed coordinates
249 Real dx1, dx2, dx3, dy1, dy2, dy3;
253 dx3 = tx1 - tx2 + tx4 - tx3;
257 dy3 = ty1 - ty2 + ty4 - ty3;
259 /* Is the mapping affine? */
260 if ((dx3 == 0.0) && (dy3 == 0.0))
262 matrix[0][0] = tx2 - tx1;
263 matrix[0][1] = tx4 - tx2;
265 matrix[1][0] = ty2 - ty1;
266 matrix[1][1] = ty4 - ty2;
275 det1 = dx3 * dy2 - dy3 * dx2;
276 det2 = dx1 * dy2 - dy1 * dx2;
278 if (det1 == 0.0 && det2 == 0.0)
281 matrix[2][0] = det1 / det2;
283 det1 = dx1 * dy3 - dy1 * dx3;
285 if (det1 == 0.0 && det2 == 0.0)
288 matrix[2][1] = det1 / det2;
290 matrix[0][0] = tx2 - tx1 + matrix[2][0] * tx2;
291 matrix[0][1] = tx3 - tx1 + matrix[2][1] * tx3;
294 matrix[1][0] = ty2 - ty1 + matrix[2][0] * ty2;
295 matrix[1][1] = ty3 - ty1 + matrix[2][1] * ty3;
303 Real scaletrans[3][3]={
304 { scalex, 0, -x1*scalex },
305 { 0, scaley, -y1*scaley },
311 for (int i = 0; i < 3; i++)
317 for (int j = 0; j < 3; j++)
319 matrix[i][j] = t1 * scaletrans[0][j];
320 matrix[i][j] += t2 * scaletrans[1][j];
321 matrix[i][j] += t3 * scaletrans[2][j];
325 mat3_invert(matrix, inv_matrix);
327 gimp_matrix3_identity (result);
328 gimp_matrix3_translate (result, -x1, -y1);
329 gimp_matrix3_scale (result, scalex, scaley);
330 gimp_matrix3_mult (&matrix, result);
335 Warp::set_param(const String & param, const ValueBase &value)
337 IMPORT_PLUS(src_tl,sync());
338 IMPORT_PLUS(src_br,sync());
339 IMPORT_PLUS(dest_tl,sync());
340 IMPORT_PLUS(dest_tr,sync());
341 IMPORT_PLUS(dest_bl,sync());
342 IMPORT_PLUS(dest_br,sync());
350 Warp::get_param(const String ¶m)const
368 Warp::get_param_vocab()const
372 ret.push_back(ParamDesc("src_tl")
373 .set_local_name(_("Source TL"))
375 .set_description(_("Top Left corner of the source to warp"))
378 ret.push_back(ParamDesc("src_br")
379 .set_local_name(_("Source BR"))
380 .set_description(_("Bottom Right corner of the source to warp"))
383 ret.push_back(ParamDesc("dest_tl")
384 .set_local_name(_("Dest TL"))
385 .set_connect("dest_tr")
386 .set_description(_("Top Left corner of the destination"))
389 ret.push_back(ParamDesc("dest_tr")
390 .set_local_name(_("Dest TR"))
391 .set_connect("dest_br")
392 .set_description(_("Top Right corner of the destination"))
395 ret.push_back(ParamDesc("dest_br")
396 .set_local_name(_("Dest BR"))
397 .set_connect("dest_bl")
398 .set_description(_("Bottom Right corner of the destination"))
401 ret.push_back(ParamDesc("dest_bl")
402 .set_local_name(_("Dest BL"))
403 .set_connect("dest_tl")
404 .set_description(_("Bottom Left corner of the destination"))
407 ret.push_back(ParamDesc("clip")
408 .set_local_name(_("Clip"))
411 ret.push_back(ParamDesc("horizon")
412 .set_local_name(_("Horizon"))
413 .set_description(_("Height that determines the horizon in perspectives"))
420 class Warp_Trans : public Transform
422 etl::handle<const Warp> layer;
424 Warp_Trans(const Warp* x):Transform(x->get_guid()),layer(x) { }
426 synfig::Vector perform(const synfig::Vector& x)const
428 return layer->transform_backward(x);
429 //Point pos(x-layer->origin);
430 //return Point(layer->cos_val*pos[0]-layer->sin_val*pos[1],layer->sin_val*pos[0]+layer->cos_val*pos[1])+layer->origin;
433 synfig::Vector unperform(const synfig::Vector& x)const
436 return layer->transform_forward(x);
437 //Point pos(x-layer->origin);
438 //return Point(layer->cos_val*pos[0]+layer->sin_val*pos[1],-layer->sin_val*pos[0]+layer->cos_val*pos[1])+layer->origin;
441 etl::handle<Transform>
442 Warp::get_transform()const
444 return new Warp_Trans(this);
447 synfig::Layer::Handle
448 Warp::hit_check(synfig::Context context, const synfig::Point &p)const
450 Point newpos(transform_forward(p));
454 Rect rect(src_tl,src_br);
455 if(!rect.is_inside(newpos))
459 return context.hit_check(newpos);
463 Warp::get_color(Context context, const Point &p)const
465 Point newpos(transform_forward(p));
469 Rect rect(src_tl,src_br);
470 if(!rect.is_inside(newpos))
471 return Color::alpha();
474 const float z(transform_backward_z(newpos));
476 return context.get_color(newpos);
478 return Color::alpha();
481 //#define ACCEL_WARP_IS_BROKEN 1
484 Warp::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
486 SuperCallback stageone(cb,0,9000,10000);
487 SuperCallback stagetwo(cb,9000,10000,10000);
489 Real pw=(renddesc.get_w())/(renddesc.get_br()[0]-renddesc.get_tl()[0]);
490 Real ph=(renddesc.get_h())/(renddesc.get_br()[1]-renddesc.get_tl()[1]);
492 if(cb && !cb->amount_complete(0,10000))
495 Point tl(renddesc.get_tl());
496 Point br(renddesc.get_br());
500 Rect render_rect(tl,br);
501 Rect clip_rect(Rect::full_plane());
502 Rect dest_rect(dest_tl,dest_br); dest_rect.expand(dest_tr).expand(dest_bl);
504 Real zoom_factor(1.0);
506 // Quick exclusion clip, if necessary
507 if(clip && !intersect(render_rect,dest_rect))
509 surface->set_wh(renddesc.get_w(),renddesc.get_h());
515 Rect other(render_rect);
519 Point min(other.get_min());
520 Point max(other.get_max());
522 bool init_point_set=false;
524 // Point trans_point[4];
527 Real z,minz(10000000000000.0f),maxz(0);
529 //! \todo checking the 4 corners for 0<=z<horizon*2 and using
530 //! only 4 corners which satisfy this condition isn't the
531 //! right thing to do. It's possible that none of the 4
532 //! corners fall within that range, and yet content of the
534 p=transform_forward(min);
535 z=transform_backward_z(p);
536 if(z>0 && z<horizon*2)
539 bounding_rect.expand(p);
541 bounding_rect=Rect(p);
543 maxz=std::max(maxz,z);
544 minz=std::min(minz,z);
547 p=transform_forward(max);
548 z=transform_backward_z(p);
549 if(z>0 && z<horizon*2)
552 bounding_rect.expand(p);
554 bounding_rect=Rect(p);
556 maxz=std::max(maxz,z);
557 minz=std::min(minz,z);
562 p=transform_forward(min);
563 z=transform_backward_z(p);
564 if(z>0 && z<horizon*2)
567 bounding_rect.expand(p);
569 bounding_rect=Rect(p);
571 maxz=std::max(maxz,z);
572 minz=std::min(minz,z);
575 p=transform_forward(max);
576 z=transform_backward_z(p);
577 if(z>0 && z<horizon*2)
580 bounding_rect.expand(p);
582 bounding_rect=Rect(p);
584 maxz=std::max(maxz,z);
585 minz=std::min(minz,z);
590 surface->set_wh(renddesc.get_w(),renddesc.get_h());
594 zoom_factor=(1+(maxz-minz));
598 #ifdef ACCEL_WARP_IS_BROKEN
599 return Layer::accelerated_render(context,surface,quality,renddesc, cb);
604 .expand(transform_forward(tl))
605 .expand(transform_forward(br))
609 //synfig::warning("given window: [%f,%f]-[%f,%f] %dx%d",tl[0],tl[1],br[0],br[1],renddesc.get_w(),renddesc.get_h());
610 //synfig::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]);
612 // If we are clipping, then go ahead and clip to the
615 clip_rect&=Rect(src_tl,src_br);
617 // Bound ourselves to the bounding rectangle of
619 clip_rect&=context.get_full_bounding_rect();//.expand_x(abs(zoom_factor/pw)).expand_y(abs(zoom_factor/ph));
621 bounding_rect&=clip_rect;
623 Point min_point(bounding_rect.get_min());
624 Point max_point(bounding_rect.get_max());
626 // we're going to divide by the difference of these pairs soon;
627 // if they're the same, we'll be dividing by zero, and we don't
629 // \todo what should we do in this case?
630 if (min_point[0] == max_point[0]) max_point[0] += 0.001;
631 if (min_point[1] == max_point[1]) max_point[1] += 0.001;
656 const int tmp_d(max(renddesc.get_w(),renddesc.get_h()));
657 Real src_pw=(tmp_d*zoom_factor)/(br[0]-tl[0]);
658 Real src_ph=(tmp_d*zoom_factor)/(br[1]-tl[1]);
661 RendDesc desc(renddesc);
663 //desc.set_flags(RendDesc::PX_ASPECT);
666 desc.set_wh(ceil_to_int(src_pw*(br[0]-tl[0])),ceil_to_int(src_ph*(br[1]-tl[1])));
668 //synfig::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());
669 if(desc.get_w()==0 && desc.get_h()==0)
671 surface->set_wh(renddesc.get_w(),renddesc.get_h());
676 // Recalculate the pixel widths for the src renddesc
677 src_pw=(desc.get_w())/(desc.get_br()[0]-desc.get_tl()[0]);
678 src_ph=(desc.get_h())/(desc.get_br()[1]-desc.get_tl()[1]);
682 source.set_wh(desc.get_w(),desc.get_h());
684 if(!context.accelerated_render(&source,quality,desc,&stageone))
687 surface->set_wh(renddesc.get_w(),renddesc.get_h());
690 Surface::pen pen(surface->begin());
698 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)
700 for(x=0,point[0]=renddesc.get_tl()[0];x<surface->get_w();x++,pen.inc_x(),point[0]+=1.0/pw)
702 tmp=transform_forward(point);
703 const float z(transform_backward_z(tmp));
704 if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
706 (*surface)[y][x]=Color::alpha();
710 u=(tmp[0]-tl[0])*src_pw;
711 v=(tmp[1]-tl[1])*src_ph;
713 if(u<0 || v<0 || u>=source.get_w() || v>=source.get_h() || isnan(u) || isnan(v))
714 (*surface)[y][x]=context.get_color(tmp);
716 (*surface)[y][x]=source.cubic_sample(u,v);
720 if(!stagetwo.amount_complete(y,surface->get_h()))
728 // INTERPOLATION_LINEAR
732 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)
734 for(x=0,point[0]=renddesc.get_tl()[0];x<surface->get_w();x++,pen.inc_x(),point[0]+=1.0/pw)
736 tmp=transform_forward(point);
737 const float z(transform_backward_z(tmp));
738 if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
740 (*surface)[y][x]=Color::alpha();
744 u=(tmp[0]-tl[0])*src_pw;
745 v=(tmp[1]-tl[1])*src_ph;
747 if(u<0 || v<0 || u>=source.get_w() || v>=source.get_h() || isnan(u) || isnan(v))
748 (*surface)[y][x]=context.get_color(tmp);
750 (*surface)[y][x]=source.linear_sample(u,v);
754 if(!stagetwo.amount_complete(y,surface->get_h()))
765 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)
767 for(x=0,point[0]=renddesc.get_tl()[0];x<surface->get_w();x++,pen.inc_x(),point[0]+=1.0/pw)
769 tmp=transform_forward(point);
770 const float z(transform_backward_z(tmp));
771 if(!clip_rect.is_inside(tmp) || !(z>0 && z<horizon))
773 (*surface)[y][x]=Color::alpha();
777 u=(tmp[0]-tl[0])*src_pw;
778 v=(tmp[1]-tl[1])*src_ph;
780 if(u<0 || v<0 || u>=source.get_w() || v>=source.get_h() || isnan(u) || isnan(v))
781 (*surface)[y][x]=context.get_color(tmp);
783 //pen.set_value(source[v][u]);
784 (*surface)[y][x]=source[floor_to_int(v)][floor_to_int(u)];
788 if(!stagetwo.amount_complete(y,surface->get_h()))
796 if(cb && !cb->amount_complete(10000,10000)) return false;
802 Warp::get_bounding_rect()const
804 return Rect::full_plane();
808 Warp::get_full_bounding_rect(Context context)const
810 // return Rect::full_plane();
812 Rect under(context.get_full_bounding_rect());
816 under&=Rect(src_tl,src_br);
819 return get_transform()->perform(under);
822 Rect under(context.get_full_bounding_rect());
823 Rect ret(Rect::zero());
825 if(under.area()==HUGE_VAL)
826 return Rect::full_plane();
855 if(ret.area()==HUGE_VAL)
856 return Rect::full_plane();