Add parameter static option for rest of layers.
[synfig.git] / synfig-core / src / synfig / layer_bitmap.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file layer_bitmap.cpp
3 **      \brief Template Header
4 **
5 **      $Id$
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 "layer_bitmap.h"
33 #include "layer.h"
34 #include "time.h"
35 #include "string.h"
36 #include "vector.h"
37
38 #include "context.h"
39 #include "time.h"
40 #include "color.h"
41 #include "surface.h"
42 #include "renddesc.h"
43 #include "target.h"
44
45 #include "general.h"
46 #include "paramdesc.h"
47 #include <ETL/misc>
48
49 #endif
50
51 /* === U S I N G =========================================================== */
52
53 using namespace synfig;
54 using namespace std;
55 using namespace etl;
56
57 /* === G L O B A L S ======================================================= */
58
59 /* === P R O C E D U R E S ================================================= */
60
61 /* === M E T H O D S ======================================================= */
62
63 synfig::Layer_Bitmap::Layer_Bitmap():
64     Layer_Composite     (1.0,Color::BLEND_COMPOSITE),
65         tl                              (-0.5,0.5),
66         br                              (0.5,-0.5),
67         c                               (1),
68         surface                 (128,128),
69         trimmed                 (false),
70         gamma_adjust    (1.0)
71 {
72         Layer::Vocab voc(get_param_vocab());
73         Layer::fill_static(voc);
74         set_param_static("c", true);
75 }
76
77 bool
78 synfig::Layer_Bitmap::set_param(const String & param, ValueBase value)
79 {
80         IMPORT(tl);
81         IMPORT(br);
82         IMPORT(c);
83         if(param=="gamma_adjust"&& value.get_type()==ValueBase::TYPE_REAL)
84         {
85                 set_param_static(param, value.get_static());
86                 gamma_adjust=1.0/value.get(Real());
87                 //gamma_adjust.set_gamma(1.0/value.get(Real()));
88                 return true;
89         }
90
91         return Layer_Composite::set_param(param,value);
92 }
93
94 ValueBase
95 synfig::Layer_Bitmap::get_param(const String & param)const
96 {
97         EXPORT(tl);
98         EXPORT(br);
99         EXPORT(c);
100         if(param=="gamma_adjust")
101         {
102                 ValueBase ret(1.0/gamma_adjust);
103                 ret.set_static(get_param_static(param));
104                 return ret;
105         }
106
107         if(param=="_width")
108         {
109                 ValueBase ret1(ValueBase::TYPE_INTEGER);
110                 ret1=int(width);
111                 ValueBase ret2(surface.get_w());
112                 ret1.set_static(get_param_static(param));
113                 ret2.set_static(get_param_static(param));
114                 if (trimmed) return ret1;
115                 return ret2;
116         }
117         if(param=="_height")
118         {
119                 ValueBase ret1(ValueBase::TYPE_INTEGER);
120                 ret1=int(height);
121                 ValueBase ret2(surface.get_h());
122                 ret1.set_static(get_param_static(param));
123                 ret2.set_static(get_param_static(param));
124                 if (trimmed) return ret1;
125                 return ret2;
126         }
127
128         return Layer_Composite::get_param(param);
129 }
130
131 Layer::Vocab
132 Layer_Bitmap::get_param_vocab()const
133 {
134         Layer::Vocab ret(Layer_Composite::get_param_vocab());
135
136         ret.push_back(ParamDesc("tl")
137                 .set_local_name(_("Top-Left"))
138                 .set_description(_("Upper left-hand Corner of image"))
139         );
140
141         ret.push_back(ParamDesc("br")
142                 .set_local_name(_("Bottom-Right"))
143                 .set_description(_("Lower right-hand Corner of image"))
144         );
145
146         ret.push_back(ParamDesc("c")
147                 .set_local_name(_("Interpolation"))
148                 .set_description(_("What type of interpolation to use"))
149                 .set_hint("enum")
150                 .add_enum_value(0,"nearest",_("Nearest Neighbor"))
151                 .add_enum_value(1,"linear",_("Linear"))
152                 .add_enum_value(2,"cosine",_("Cosine"))
153                 .add_enum_value(3,"cubic",_("Cubic"))
154         );
155
156         ret.push_back(ParamDesc("gamma_adjust")
157                 .set_local_name(_("Gamma Adjustment"))
158         );
159
160         return ret;
161 }
162
163 synfig::Layer::Handle
164 Layer_Bitmap::hit_check(synfig::Context context, const synfig::Point &pos)const
165 {
166         Point surface_pos;
167         surface_pos=pos-tl;
168
169         surface_pos[0]/=br[0]-tl[0];
170         if(surface_pos[0]<=1.0 && surface_pos[0]>=0.0)
171         {
172                 surface_pos[1]/=br[1]-tl[1];
173                 if(surface_pos[1]<=1.0 && surface_pos[1]>=0.0)
174                 {
175                         return const_cast<Layer_Bitmap*>(this);
176                 }
177         }
178
179         return context.hit_check(pos);
180 }
181
182 inline
183 const Color&
184 synfig::Layer_Bitmap::filter(Color& x)const
185 {
186         if(gamma_adjust!=1.0)
187         {
188                 x.set_r(powf((float)x.get_r(),gamma_adjust));
189                 x.set_g(powf((float)x.get_g(),gamma_adjust));
190                 x.set_b(powf((float)x.get_b(),gamma_adjust));
191         }
192         return x;
193 }
194
195 Color
196 synfig::Layer_Bitmap::get_color(Context context, const Point &pos)const
197 {
198         Point surface_pos;
199
200         if(!get_amount())
201                 return context.get_color(pos);
202
203         surface_pos=pos-tl;
204
205         surface_pos[0]/=br[0]-tl[0];
206         if(surface_pos[0]<=1.0 && surface_pos[0]>=0.0)
207         {
208                 surface_pos[1]/=br[1]-tl[1];
209                 if(surface_pos[1]<=1.0 && surface_pos[1]>=0.0)
210                 {
211                         if (trimmed)
212                         {
213                                 surface_pos[0]*=width;
214                                 surface_pos[1]*=height;
215
216                                 if (surface_pos[0] > left+surface.get_w() || surface_pos[0] < left || surface_pos[1] > top+surface.get_h() || surface_pos[1] < top)
217                                         return context.get_color(pos);
218
219                                 surface_pos[0] -= left;
220                                 surface_pos[1] -= top;
221                         }
222                         else
223                         {
224                                 surface_pos[0]*=surface.get_w();
225                                 surface_pos[1]*=surface.get_h();
226                         }
227
228                         Color ret(Color::alpha());
229
230                         switch(c)
231                         {
232                         case 6: // Undefined
233                         case 5: // Undefined
234                         case 4: // Undefined
235                         case 3: // Cubic
236                                 ret=surface.cubic_sample(surface_pos[0],surface_pos[1]);
237                                 break;
238
239                         case 2: // Cosine
240                                 ret=surface.cosine_sample(surface_pos[0],surface_pos[1]);
241                                 break;
242                         case 1: // Linear
243                                 ret=surface.linear_sample(surface_pos[0],surface_pos[1]);
244                                 break;
245                         case 0: // Nearest Neighbor
246                         default:
247                                 {
248                                         int x(min(surface.get_w()-1,max(0,round_to_int(surface_pos[0]))));
249                                         int y(min(surface.get_h()-1,max(0,round_to_int(surface_pos[1]))));
250                                         ret= surface[y][x];
251                                 }
252                         break;
253                         }
254
255                         ret=filter(ret);
256
257                         if(get_amount()==1 && get_blend_method()==Color::BLEND_STRAIGHT)
258                                 return ret;
259                         else
260                                 return Color::blend(ret,context.get_color(pos),get_amount(),get_blend_method());
261                 }
262         }
263
264         return context.get_color(pos);
265 }
266
267 bool
268 Layer_Bitmap::accelerated_render(Context context,Surface *out_surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)  const
269 {
270         int interp=c;
271         if(quality>=10)
272                 interp=0;
273         else if(quality>=5 && interp>1)
274                 interp=1;
275
276         // We can only handle NN and Linear at the moment
277         //if(interp>1)
278         //      return Layer_Composite::accelerated_render(context,out_surface,quality,renddesc,cb);
279
280         //if we don't actually have a valid surface just skip us
281         if(!surface.is_valid())
282         {
283                 // Render what is behind us
284                 return context.accelerated_render(out_surface,quality,renddesc,cb);
285         }
286
287         SuperCallback subcb(cb,1,10000,10001+renddesc.get_h());
288
289         if(     get_amount()==1 &&
290                 get_blend_method()==Color::BLEND_STRAIGHT &&
291                 !trimmed &&
292                 renddesc.get_tl()==tl &&
293                 renddesc.get_br()==br)
294         {
295                 // Check for the trivial case
296                 if(surface.get_w()==renddesc.get_w() && surface.get_h()==renddesc.get_h() && gamma_adjust==1.0f)
297                 {
298                         if(cb && !cb->amount_complete(0,100)) return false;
299                         *out_surface=surface;
300                         if(cb && !cb->amount_complete(100,100)) return false;
301                         return true;
302                 }
303                 out_surface->set_wh(renddesc.get_w(),renddesc.get_h());
304         }
305         else
306         {
307                 // Render what is behind us
308                 if(!context.accelerated_render(out_surface,quality,renddesc,&subcb))
309                         return false;
310         }
311
312         if(cb && !cb->amount_complete(10000,10001+renddesc.get_h())) return false;
313
314         Point   obr     = renddesc.get_br(),
315                         otl = renddesc.get_tl();
316
317         //Vector::value_type pw=renddesc.get_w()/(renddesc.get_br()[0]-renddesc.get_tl()[0]);
318         //Vector::value_type ph=renddesc.get_h()/(renddesc.get_br()[1]-renddesc.get_tl()[1]);
319
320         //A = representation of input (just tl,br)      //just a scaling right now
321         //B = representation of output (just tl,br)     //just a scaling right now
322         //sa = scaling for input (0,1) -> (0,w/h)
323         //sb = scaling for output (0,1) -> (0,w/h)
324
325         float   outwf = obr[0] - otl[0];
326         float   outhf = obr[1] - otl[1];
327
328         int             inw = surface.get_w();
329         int             inh = surface.get_h();
330
331         int             outw = renddesc.get_w();
332         int             outh = renddesc.get_h();
333
334         float   inwf, inhf;
335         Point   itl, ibr;
336
337         if (trimmed)
338         {
339                 inwf = (br[0] - tl[0])*surface.get_w()/width;
340                 inhf = (br[1] - tl[1])*surface.get_h()/height;
341                 itl = Point(tl[0] + (br[0]-tl[0])*left/width,
342                                         tl[1] + (br[1]-tl[1])*top/height);
343                 ibr = Point(tl[0] + (br[0]-tl[0])*(left+inw)/width,
344                                         tl[1] + (br[1]-tl[1])*(top+inh)/height);
345         }
346         else
347         {
348                 inwf = br[0] - tl[0];
349                 inhf = br[1] - tl[1];
350                 itl = tl;
351                 ibr = br;
352         }
353
354         //need to get the input coords in output space, so we can clip
355
356         //get the desired corners of the bitmap (in increasing order) in integers
357         //floating point corners
358         float x1f = (itl[0] - otl[0])*outw/outwf;
359         float x2f = (ibr[0] - otl[0])*outw/outwf;
360         float y1f = (itl[1] - otl[1])*outh/outhf;
361         float y2f = (ibr[1] - otl[1])*outh/outhf;
362
363         if(x1f > x2f) swap(x1f,x2f);
364         if(y1f > y2f) swap(y1f,y2f);
365
366         int x_start = max(0,(int)floor(x1f));   //probably floor
367         int x_end       = min(outw,(int)ceil(x2f));     //probably ceil
368         int y_start = max(0,(int)floor(y1f));   //probably floor
369         int y_end       = min(outh,(int)ceil(y2f));     //probably ceil
370
371         //need to get the x,y,dx,dy values from output space to input, so we can do fast interpolation
372
373         //get the starting position in input space... for interpolating
374
375         // in int -> out float:
376         // Sb(B^-1)A(Sa^-1) x
377         float inx_start = (((x_start/*+0.5f*/)*outwf/outw + otl[0]) - itl[0])*inw/inwf; //may want to bias this (center of pixel)???
378         float iny_start = (((y_start/*+0.5f*/)*outhf/outh + otl[1]) - itl[1])*inh/inhf; //may want to bias this (center of pixel)???
379
380         //calculate the delta values in input space for one pixel movement in output space
381         //same matrix but with a vector instead of a point...
382         float indx = outwf*(inw)/((outw)*inwf);         //translations died
383         float indy = outhf*(inh)/((outh)*inhf);         //translations died
384
385         //perhaps use a DDA algorithm... if faster...
386         //   will still want pixel fractions to be floating point since colors are
387
388         //synfig::info("xstart:%d ystart:%d xend:%d yend:%d",x_start,y_start,x_end,y_end);
389
390         //start drawing at the start of the bitmap (either origin or corner of input...)
391         //and get other info
392         Surface::alpha_pen pen(out_surface->get_pen(x_start,y_start));
393         pen.set_alpha(get_amount());
394         pen.set_blend_method(get_blend_method());
395
396         //check if we should use the downscale filtering
397         if(quality <= 7)
398         {
399                 //the stride of the value should be inverted because we want to downsample
400                 //when the stride is small, not big
401                 //int multw = (int)ceil(indx);
402                 //int multh = (int)ceil(indy);
403
404                 if(indx > 1.7 || indy > 1.7)
405                 {
406                         /*synfig::info("Decided to downsample? ratios - (%f,%f) -> (%d,%d)",
407                                                 indx, indy, multw, multh);      */
408
409                         //use sample rect here...
410
411                         float iny, inx;
412                         int x,y;
413
414                         //Point sample - truncate
415                         iny = iny_start;//+0.5f;
416                         for(y = y_start; y < y_end; ++y, pen.inc_y(), iny += indy)
417                         {
418                                 inx = inx_start;//+0.5f;
419                                 for(x = x_start; x < x_end; x++, pen.inc_x(), inx += indx)
420                                 {
421                                         Color rc = surface.sample_rect_clip(inx,iny,inx+indx,iny+indy);
422                                         pen.put_value(filter(rc));
423                                 }
424                                 pen.dec_x(x_end-x_start);
425                         }
426
427                         //Color c = (*out_surface)[0][0];
428                         //synfig::info("ValueBase of first pixel = (%f,%f,%f,%f)",c.get_r(),c.get_g(),c.get_b(),c.get_a());
429
430                         return true;
431                 }
432         }
433
434         //perform normal interpolation
435         if(interp==0)
436         {
437                 //synfig::info("Decided to do nearest neighbor");
438                 float iny, inx;
439                 int x,y;
440
441                 //Point sample - truncate
442                 iny = iny_start;//+0.5f;
443                 for(y = y_start; y < y_end; y++, pen.inc_y(), iny += indy)
444                 {
445                         inx = inx_start;//+0.5f;
446                         int yclamp = min(inh-1, max(0, round_to_int(iny)));
447                         for(x = x_start; x < x_end; x++, pen.inc_x(), inx += indx)
448                         {
449                                 int xclamp = min(inw-1, max(0, round_to_int(inx)));
450                                 Color c = filter(surface[yclamp][xclamp]);
451                                 pen.put_value(c); //must get rid of the clip
452                         }
453                         pen.dec_x(x_end-x_start);
454                 }
455         }
456         else
457         if(interp==1)
458         {
459                 //bilinear filtering
460
461                 //float         xmf,xpf,ymf,ypf;
462                 //int           xm,xp,ym,yp;
463                 float   inx,iny;
464                 int             x,y;
465
466                 //can probably buffer for x values...
467
468                 //loop and based on inx,iny sample input image
469                 iny = iny_start;
470                 for(y = y_start; y < y_end; y++, pen.inc_y(), iny += indy)
471                 {
472                         inx = inx_start;
473                         for(x = x_start; x < x_end; x++, pen.inc_x(), inx += indx)
474                         {
475                                 Color col(surface.linear_sample(inx,iny));
476                                 pen.put_value(filter(col));
477                         }
478                         pen.dec_x(x_end-x_start);
479
480                 }
481         }
482         else
483         if(interp==2)
484         {
485                 //cosine filtering
486
487                 //float         xmf,xpf,ymf,ypf;
488                 //int           xm,xp,ym,yp;
489                 float   inx,iny;
490                 int             x,y;
491
492                 //can probably buffer for x values...
493
494                 //loop and based on inx,iny sample input image
495                 iny = iny_start;
496                 for(y = y_start; y < y_end; y++, pen.inc_y(), iny += indy)
497                 {
498                         inx = inx_start;
499                         for(x = x_start; x < x_end; x++, pen.inc_x(), inx += indx)
500                         {
501                                 Color col(surface.cosine_sample(inx,iny));
502                                 pen.put_value(filter(col));
503                         }
504                         pen.dec_x(x_end-x_start);
505
506                 }
507         }
508         else
509         {
510                 //cubic filtering
511
512                 //float         xmf,xpf,ymf,ypf;
513                 //int           xm,xp,ym,yp;
514                 float   inx,iny;
515                 int             x,y;
516
517                 //can probably buffer for x values...
518
519                 //loop and based on inx,iny sample input image
520                 iny = iny_start;
521                 for(y = y_start; y < y_end; y++, pen.inc_y(), iny += indy)
522                 {
523                         inx = inx_start;
524                         for(x = x_start; x < x_end; x++, pen.inc_x(), inx += indx)
525                         {
526                                 Color col(surface.cubic_sample(inx,iny));
527                                 pen.put_value(filter(col));
528                         }
529                         pen.dec_x(x_end-x_start);
530
531                 }
532         }
533
534         return true;
535 }
536
537 Rect
538 Layer_Bitmap::get_bounding_rect()const
539 {
540         return Rect(tl,br);
541 }