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