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