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