marked stable
[synfig.git] / synfig-core / tags / stable / src / modules / lyr_std / shade.cpp
1 /* === S I N F G =========================================================== */
2 /*!     \file shade.cpp
3 **      \brief Template Header
4 **
5 **      $Id: shade.cpp,v 1.2 2005/01/24 03:08:17 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **
10 **      This software and associated documentation
11 **      are CONFIDENTIAL and PROPRIETARY property of
12 **      the above-mentioned copyright holder.
13 **
14 **      You may not copy, print, publish, or in any
15 **      other way distribute this software without
16 **      a prior written agreement with
17 **      the copyright holder.
18 **      \endlegal
19 */
20 /* ========================================================================= */
21
22 /* === H E A D E R S ======================================================= */
23
24 #ifdef USING_PCH
25 #       include "pch.h"
26 #else
27 #ifdef HAVE_CONFIG_H
28 #       include <config.h>
29 #endif
30
31 #include "shade.h"
32
33 #include <sinfg/string.h>
34 #include <sinfg/time.h>
35 #include <sinfg/context.h>
36 #include <sinfg/paramdesc.h>
37 #include <sinfg/renddesc.h>
38 #include <sinfg/surface.h>
39 #include <sinfg/value.h>
40 #include <sinfg/valuenode.h>
41 #include <sinfg/segment.h>
42
43 #include <cstring>
44 #include <ETL/pen>
45 #include <ETL/misc>
46
47 #endif
48
49 using namespace sinfg;
50 using namespace etl;
51 using namespace std;
52
53 /*#define TYPE_BOX                      0
54 #define TYPE_FASTGUASSIAN       1
55 #define TYPE_FASTGAUSSIAN       1
56 #define TYPE_CROSS                      2
57 #define TYPE_GUASSIAN           3
58 #define TYPE_GAUSSIAN           3
59 #define TYPE_DISC                       4
60 */
61
62 /* -- G L O B A L S --------------------------------------------------------- */
63
64 SINFG_LAYER_INIT(Layer_Shade);
65 SINFG_LAYER_SET_NAME(Layer_Shade,"shade");
66 SINFG_LAYER_SET_LOCAL_NAME(Layer_Shade,_("Shade"));
67 SINFG_LAYER_SET_CATEGORY(Layer_Shade,_("Stylize"));
68 SINFG_LAYER_SET_VERSION(Layer_Shade,"0.2");
69 SINFG_LAYER_SET_CVS_ID(Layer_Shade,"$Id: shade.cpp,v 1.2 2005/01/24 03:08:17 darco Exp $");
70
71 /* -- F U N C T I O N S ----------------------------------------------------- */
72
73 inline void clamp(sinfg::Vector &v)
74 {
75         if(v[0]<0.0)v[0]=0.0;
76         if(v[1]<0.0)v[1]=0.0;
77 }
78
79 Layer_Shade::Layer_Shade():
80         Layer_Composite (0.75,Color::BLEND_BEHIND),
81         size(0.1,0.1),
82         type(Blur::FASTGAUSSIAN),
83         color(Color::black()),
84         offset(0.2,-0.2),
85         invert(false)
86 {
87 }
88
89 bool
90 Layer_Shade::set_param(const String &param, const ValueBase &value)
91 {
92         IMPORT_PLUS(size,clamp(size));
93         IMPORT(type);
94         IMPORT(color);
95         IMPORT(offset);
96         IMPORT(invert);
97         
98         return Layer_Composite::set_param(param,value);
99 }
100
101 ValueBase
102 Layer_Shade::get_param(const String &param)const
103 {
104         EXPORT(size);
105         EXPORT(type);
106         EXPORT(color);
107         EXPORT(offset);
108         EXPORT(invert);
109         
110         EXPORT_NAME();
111         EXPORT_VERSION();
112                 
113         return Layer_Composite::get_param(param);       
114 }
115
116 Color
117 Layer_Shade::get_color(Context context, const Point &pos)const
118 {
119         Point blurpos = Blur(size,type)(pos);
120
121         if(get_amount()==0.0)
122                 return context.get_color(pos);
123         
124         Color shade(color);
125
126         if(!invert)
127                 shade.set_a(context.get_color(blurpos-offset).get_a());
128         else
129                 shade.set_a(1.0f-context.get_color(blurpos-offset).get_a());
130
131         return Color::blend(shade,context.get_color(pos),get_amount(),get_blend_method());
132 }
133
134 bool
135 Layer_Shade::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
136 {
137         int x,y;
138         
139         const int       w = renddesc.get_w(),
140                                 h = renddesc.get_h();
141         const Real      pw = renddesc.get_pw(),
142                                 ph = renddesc.get_ph();
143         
144         RendDesc        workdesc(renddesc);
145         Surface         worksurface;
146         etl::surface<float> blurred;
147                         
148         //expand the working surface to accommodate the blur
149         
150         //the expanded size = 1/2 the size in each direction rounded up
151         int     halfsizex = (int) (abs(size[0]*.5/pw) + 3),
152                 halfsizey = (int) (abs(size[1]*.5/ph) + 3);
153
154         int offset_u(-round_to_int(offset[0]/pw)),offset_v(-round_to_int(offset[1]/ph));
155
156         int offset_w(w+abs(offset_u)),offset_h(h+abs(offset_v));
157
158         workdesc.set_subwindow(
159                 offset_u<0?offset_u:0,
160                 offset_v<0?offset_v:0,
161                 (offset_u>0?offset_u:0)+w,
162                 (offset_v>0?offset_v:0)+h
163         );
164
165         /*
166         if(quality >=10)
167         {
168                 halfsizex=1;
169                 halfsizey=1;
170         }else
171         */
172         if(quality == 9)
173         {
174                 halfsizex/=4;
175                 halfsizey/=4;
176         }
177         
178         //expand by 1/2 size in each direction on either side
179         switch(type)
180         {
181                 case Blur::DISC:
182                 case Blur::BOX:
183                 case Blur::CROSS:
184                 {
185                         workdesc.set_subwindow(-max(1,halfsizex),-max(1,halfsizey),offset_w+2*max(1,halfsizex),offset_h+2*max(1,halfsizey));
186                         break;
187                 }
188                 case Blur::FASTGAUSSIAN:
189                 {
190                         if(quality < 4)
191                         {
192                                 halfsizex*=2;
193                                 halfsizey*=2;
194                         }
195                         workdesc.set_subwindow(-max(1,halfsizex),-max(1,halfsizey),offset_w+2*max(1,halfsizex),offset_h+2*max(1,halfsizey));
196                         break;
197                 }
198                 case Blur::GAUSSIAN:
199                 {
200                 #define GAUSSIAN_ADJUSTMENT             (0.05)
201                         Real    pw = (Real)workdesc.get_w()/(workdesc.get_br()[0]-workdesc.get_tl()[0]);
202                         Real    ph = (Real)workdesc.get_h()/(workdesc.get_br()[1]-workdesc.get_tl()[1]);
203                         
204                         pw=pw*pw;
205                         ph=ph*ph;
206
207                         halfsizex = (int)(abs(pw)*size[0]*GAUSSIAN_ADJUSTMENT+0.5);
208                         halfsizey = (int)(abs(ph)*size[1]*GAUSSIAN_ADJUSTMENT+0.5);
209
210                         halfsizex = (halfsizex + 1)/2;
211                         halfsizey = (halfsizey + 1)/2;
212                         workdesc.set_subwindow( -halfsizex, -halfsizey, offset_w+2*halfsizex, offset_h+2*halfsizey );
213                                                 
214                         break;
215                 }
216         }
217 #define SCALE_FACTOR    (64.0)
218         if(/*quality>9 || */size[0]<=pw*SCALE_FACTOR)
219         {
220                 SuperCallback stageone(cb,0,5000,10000);
221                 SuperCallback stagetwo(cb,5000,10000,10000);
222                 
223                 //callbacks depend on how long the blur takes
224                 if(size[0] || size[1])
225                 {
226                         if(type == Blur::DISC)
227                         {
228                                 stageone = SuperCallback(cb,0,5000,10000);
229                                 stagetwo = SuperCallback(cb,5000,10000,10000);  
230                         }
231                         else
232                         {
233                                 stageone = SuperCallback(cb,0,9000,10000);
234                                 stagetwo = SuperCallback(cb,9000,10000,10000);  
235                         }
236                 }
237                 else
238                 {
239                         stageone = SuperCallback(cb,0,9999,10000);
240                         stagetwo = SuperCallback(cb,9999,10000,10000);  
241                 }
242
243
244
245                 //render the background onto the expanded surface
246                 if(!context.accelerated_render(&worksurface,quality,workdesc,&stageone))
247                         return false;
248         
249                 // Copy over the alpha
250                 blurred.set_wh(worksurface.get_w(),worksurface.get_h());
251                 for(int j=0;j<worksurface.get_h();j++)
252                         for(int i=0;i<worksurface.get_w();i++)
253                         {
254                                 blurred[j][i]=worksurface[j][i].get_a();
255                         }
256                 
257                 //blur the image
258                 Blur(size,type,&stagetwo)(blurred,workdesc.get_br()-workdesc.get_tl(),blurred);
259                 
260                 //be sure the surface is of the correct size
261                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
262                 
263                 int u = halfsizex-(offset_u<0?offset_u:0), v = halfsizey-(offset_v<0?offset_v:0);
264                 for(y=0;y<renddesc.get_h();y++,v++)
265                 {
266                         u = halfsizex-(offset_u<0?offset_u:0);
267                         for(x=0;x<renddesc.get_w();x++,u++)
268                         {
269                                 Color a(color);
270         
271                                 if(!invert)
272                                         a.set_a(blurred.linear_sample(offset_u+(float)u,offset_v+(float)v));
273                                 else
274                                         a.set_a(1.0f-blurred.linear_sample(offset_u+(float)u,offset_v+(float)v));
275                                 
276                                 if(a.get_a() || get_blend_method()==Color::BLEND_STRAIGHT)
277                                 {
278                                         (*surface)[y][x]=Color::blend(a,worksurface[v][u],get_amount(),get_blend_method());
279                                 }
280                                 else (*surface)[y][x] = worksurface[v][u];
281                         }
282                 }
283         }
284         else
285         {
286                 
287                 SuperCallback stageone(cb,0,5000,10000);
288                 SuperCallback stagetwo(cb,5000,10000,10000);
289                 
290                 //callbacks depend on how long the blur takes
291                 if(size[0] || size[1])
292                 {
293                         if(type == Blur::DISC)
294                         {
295                                 stageone = SuperCallback(cb,0,5000,10000);
296                                 stagetwo = SuperCallback(cb,5000,10000,10000);  
297                         }
298                         else
299                         {
300                                 stageone = SuperCallback(cb,0,9000,10000);
301                                 stagetwo = SuperCallback(cb,9000,10000,10000);  
302                         }
303                 }
304                 else
305                 {
306                         stageone = SuperCallback(cb,0,9999,10000);
307                         stagetwo = SuperCallback(cb,9999,10000,10000);  
308                 }
309
310                 int fw(floor_to_int(abs(size[0]/(pw*SCALE_FACTOR)))+1);
311                 int fh(floor_to_int(abs(size[1]/(ph*SCALE_FACTOR)))+1);
312                 int tmpw(round_to_int((float)workdesc.get_w()/fw)),tmph(round_to_int((float)workdesc.get_h()/fh));
313                 
314                 workdesc.clear_flags();
315                 workdesc.set_wh(tmpw,tmph);
316                 //sinfg::info("fw: %d, fh: %d",fw,fh);
317
318                 //render the blur fodder
319                 if(!context.accelerated_render(&worksurface,quality,workdesc,&stageone))
320                         return false;
321
322                 //render the background
323                 if(!context.accelerated_render(surface,quality,renddesc,&stageone))
324                         return false;
325
326                 // Copy over the alpha
327                 blurred.set_wh(worksurface.get_w(),worksurface.get_h());
328                 for(int j=0;j<worksurface.get_h();j++)
329                         for(int i=0;i<worksurface.get_w();i++)
330                                 blurred[j][i]=worksurface[j][i].get_a();
331                 
332                 //blur the image
333                 Blur(size,type,&stagetwo)(blurred,workdesc.get_br()-workdesc.get_tl(),blurred);
334
335
336                 int u = halfsizex-(offset_u<0?offset_u:0), v = halfsizey-(offset_v<0?offset_v:0);
337                 for(y=0;y<renddesc.get_h();y++,v++)
338                 {
339                         u = halfsizex-(offset_u<0?offset_u:0);
340                         for(x=0;x<renddesc.get_w();x++,u++)
341                         {
342                                 Color a(color);
343         
344                                 if(!invert)
345                                         a.set_a(blurred.linear_sample(((float)offset_u+(float)u)/(float)fw,((float)offset_v+(float)v)/(float)fh));
346                                 else
347                                         a.set_a(1.0f-blurred.linear_sample(((float)offset_u+(float)u)/fw,((float)offset_v+(float)v)/(float)fh));
348                                 
349                                 if(a.get_a() || get_blend_method()==Color::BLEND_STRAIGHT)
350                                 {
351                                         (*surface)[y][x]=Color::blend(a,(*surface)[y][x],get_amount(),get_blend_method());
352                                 }
353                         }
354                 }
355         }
356         
357         
358         if(cb && !cb->amount_complete(10000,10000))
359         {
360                 //if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
361                 return false;
362         }
363                 
364         return true;
365 }
366         
367 Layer::Vocab
368 Layer_Shade::get_param_vocab(void)const
369 {
370         Layer::Vocab ret(Layer_Composite::get_param_vocab());
371
372         ret.push_back(ParamDesc("color")
373                 .set_local_name(_("Color"))
374         );
375         ret.push_back(ParamDesc("offset")
376                 .set_local_name(_("Offset"))
377         );
378         ret.push_back(ParamDesc("size")
379                 .set_local_name(_("Size"))
380                 .set_description(_("Size of Shade"))
381                 .set_is_distance()
382                 .set_origin("offset")
383         );
384         ret.push_back(ParamDesc("type")
385                 .set_local_name(_("Type"))
386                 .set_description(_("Type of blur to use"))
387                 .set_hint("enum")
388                 .add_enum_value(Blur::BOX,"box",_("Box Blur"))
389                 .add_enum_value(Blur::FASTGAUSSIAN,"fastgaussian",_("Fast Gaussian Blur"))
390                 .add_enum_value(Blur::CROSS,"cross",_("Cross-Hatch Blur"))
391                 .add_enum_value(Blur::GAUSSIAN,"gaussian",_("Gaussian Blur"))
392                 .add_enum_value(Blur::DISC,"disc",_("Disc Blur"))
393         );
394         
395         ret.push_back(ParamDesc("invert")
396                 .set_local_name(_("Invert"))
397         );
398         
399         return ret;
400 }
401
402 Rect
403 Layer_Shade::get_full_bounding_rect(Context context)const
404 {
405         if(is_disabled())
406                 return context.get_full_bounding_rect();
407
408         if(invert)
409                 return Rect::full_plane();
410
411         Rect under(context.get_full_bounding_rect());
412
413         if(Color::is_onto(get_blend_method()))
414                 return under;
415
416         Rect bounds((under+offset).expand_x(size[0]).expand_y(size[1]));
417
418         if(is_solid_color())
419                 return bounds;
420                 
421         return bounds|under;
422 }