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