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