moreupdates
[synfig.git] / synfig-core / trunk / src / modules / mod_gradient / lineargradient.cpp
1 /*! ========================================================================
2 ** Synfig
3 ** Template File
4 ** $Id: lineargradient.cpp,v 1.1.1.1 2005/01/04 01:23:10 darco Exp $
5 **
6 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
7 **
8 ** This software and associated documentation
9 ** are CONFIDENTIAL and PROPRIETARY property of
10 ** the above-mentioned copyright holder.
11 **
12 ** You may not copy, print, publish, or in any
13 ** other way distribute this software without
14 ** a prior written agreement with
15 ** the copyright holder.
16 **
17 ** === N O T E S ===========================================================
18 **
19 ** ========================================================================= */
20
21 /* === H E A D E R S ======================================================= */
22
23 #ifdef USING_PCH
24 #       include "pch.h"
25 #else
26 #ifdef HAVE_CONFIG_H
27 #       include <config.h>
28 #endif
29
30 #include "lineargradient.h"
31
32 #include <synfig/string.h>
33 #include <synfig/time.h>
34 #include <synfig/context.h>
35 #include <synfig/paramdesc.h>
36 #include <synfig/renddesc.h>
37 #include <synfig/surface.h>
38 #include <synfig/value.h>
39 #include <synfig/valuenode.h>
40
41 #endif
42
43 /* === M A C R O S ========================================================= */
44
45 /* === G L O B A L S ======================================================= */
46
47 SYNFIG_LAYER_INIT(LinearGradient);
48 SYNFIG_LAYER_SET_NAME(LinearGradient,"linear_gradient");
49 SYNFIG_LAYER_SET_LOCAL_NAME(LinearGradient,_("Linear Gradient"));
50 SYNFIG_LAYER_SET_CATEGORY(LinearGradient,_("Gradients"));
51 SYNFIG_LAYER_SET_VERSION(LinearGradient,"0.0");
52 SYNFIG_LAYER_SET_CVS_ID(LinearGradient,"$Id: lineargradient.cpp,v 1.1.1.1 2005/01/04 01:23:10 darco Exp $");
53
54 /* === P R O C E D U R E S ================================================= */
55
56 /* === M E T H O D S ======================================================= */
57
58 inline void
59 LinearGradient::sync()
60 {
61         diff=(p2-p1);
62         const Real mag(diff.inv_mag());
63         diff*=mag*mag;
64 }
65
66
67 LinearGradient::LinearGradient():
68         p1(1,1),
69         p2(-1,-1),
70         gradient(Color::black(), Color::white()),
71         loop(false),
72         zigzag(false)
73 {
74         sync();
75 }
76
77 inline Color
78 LinearGradient::color_func(const Point &point, float supersample)const
79 {
80         Real dist(point*diff-p1*diff);
81         
82         if(loop)
83                 dist-=floor(dist);
84         
85         if(zigzag)
86         {
87                 dist*=2.0;
88                 supersample*=2.0;
89                 if(dist>1)dist=2.0-dist;
90         }
91
92         if(loop)
93         {
94                 if(dist+supersample*0.5>1.0)
95                 {
96                         Color pool(gradient(dist,supersample*0.5).premult_alpha()*(1.0-(dist-supersample*0.5)));
97                         pool+=gradient((dist+supersample*0.5)-1.0,supersample*0.5).premult_alpha()*((dist+supersample*0.5)-1.0);
98                         return pool.demult_alpha();
99                 }
100                 if(dist-supersample*0.5<0.0)
101                 {
102                         Color pool(gradient(dist,supersample*0.5).premult_alpha()*(dist+supersample*0.5));
103                         pool+=gradient(1.0-(dist-supersample*0.5),supersample*0.5).premult_alpha()*(-(dist-supersample*0.5));
104                         return pool.demult_alpha();
105                 }
106         }
107         return gradient(dist,supersample);
108 }
109
110 float
111 LinearGradient::calc_supersample(const synfig::Point &x, float pw,float ph)const
112 {
113         return pw/(p2-p1).mag();
114 }
115
116 synfig::Layer::Handle
117 LinearGradient::hit_check(synfig::Context context, const synfig::Point &point)const
118 {
119         if(get_blend_method()==Color::BLEND_STRAIGHT && get_amount()>=0.5)
120                 return const_cast<LinearGradient*>(this);
121         if(get_amount()==0.0)
122                 return context.hit_check(point);
123         if((get_blend_method()==Color::BLEND_STRAIGHT || get_blend_method()==Color::BLEND_COMPOSITE) && color_func(point).get_a()>0.5)
124                 return const_cast<LinearGradient*>(this);
125         return context.hit_check(point);
126 }
127
128 bool
129 LinearGradient::set_param(const String & param, const ValueBase &value)
130 {
131         if(param=="p1" && value.same_as(p1))
132         {
133                 p1=value.get(p1);
134                 sync();
135                 return true;
136         }
137         if(param=="p2" && value.same_as(p2))
138         {
139                 p2=value.get(p2);
140                 sync();
141                 return true;
142         }
143         //IMPORT(p1);
144         //IMPORT(p2);
145         
146         
147         IMPORT(gradient);
148         IMPORT(loop);
149         IMPORT(zigzag);
150         return Layer_Composite::set_param(param,value); 
151 }
152
153 ValueBase
154 LinearGradient::get_param(const String & param)const
155 {
156         EXPORT(p1);
157         EXPORT(p2);
158         EXPORT(gradient);
159         EXPORT(loop);
160         EXPORT(zigzag);
161         
162         EXPORT_NAME();
163         EXPORT_VERSION();
164                 
165         return Layer_Composite::get_param(param);       
166 }
167
168 Layer::Vocab
169 LinearGradient::get_param_vocab()const
170 {
171         Layer::Vocab ret(Layer_Composite::get_param_vocab());
172         
173         ret.push_back(ParamDesc("p1")
174                 .set_local_name(_("Point 1"))
175                 .set_connect("p2")
176         );
177         ret.push_back(ParamDesc("p2")
178                 .set_local_name(_("Point 2"))
179         );
180         ret.push_back(ParamDesc("gradient")
181                 .set_local_name(_("Gradient"))
182         );
183         ret.push_back(ParamDesc("loop")
184                 .set_local_name(_("Loop"))
185         );
186         ret.push_back(ParamDesc("zigzag")
187                 .set_local_name(_("ZigZag"))
188         );
189         
190         return ret;
191 }
192
193 Color
194 LinearGradient::get_color(Context context, const Point &point)const
195 {
196         const Color color(color_func(point));
197
198         if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
199                 return color;
200         else
201                 return Color::blend(color,context.get_color(point),get_amount(),get_blend_method());
202 }
203
204 bool
205 LinearGradient::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
206 {
207         SuperCallback supercb(cb,0,9500,10000);
208
209         if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
210         {
211                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
212         }
213         else
214         {
215                 if(!context.accelerated_render(surface,quality,renddesc,&supercb))
216                         return false;
217                 if(get_amount()==0)
218                         return true;
219         }
220
221                 
222         int x,y;
223
224         Surface::pen pen(surface->begin());
225         const Real pw(renddesc.get_pw()),ph(renddesc.get_ph());
226         Point pos;
227         Point tl(renddesc.get_tl());
228         const int w(surface->get_w());
229         const int h(surface->get_h());
230         
231         if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
232         {
233                 for(y=0,pos[1]=tl[1];y<h;y++,pen.inc_y(),pen.dec_x(x),pos[1]+=ph)
234                         for(x=0,pos[0]=tl[0];x<w;x++,pen.inc_x(),pos[0]+=pw)
235                                 pen.put_value(color_func(pos,calc_supersample(pos,pw,ph)));
236         }
237         else
238         {
239                 for(y=0,pos[1]=tl[1];y<h;y++,pen.inc_y(),pen.dec_x(x),pos[1]+=ph)
240                         for(x=0,pos[0]=tl[0];x<w;x++,pen.inc_x(),pos[0]+=pw)
241                                 pen.put_value(Color::blend(color_func(pos,calc_supersample(pos,pw,ph)),pen.get_value(),get_amount(),get_blend_method()));
242         }
243
244         // Mark our progress as finished
245         if(cb && !cb->amount_complete(10000,10000))
246                 return false;
247
248         return true;
249 }