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