Merge branch 'genete_static_values'
[synfig.git] / synfig-core / src / modules / mod_geometry / rectangle.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file rectangle.cpp
3 **      \brief Implementation of the "Rectangle" layer
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **      Copyright (c) 2007, 2008 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
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 <synfig/string.h>
34 #include <synfig/time.h>
35 #include <synfig/context.h>
36 #include <synfig/paramdesc.h>
37 #include <synfig/renddesc.h>
38 #include <synfig/surface.h>
39 #include <synfig/value.h>
40 #include <synfig/valuenode.h>
41 #include <ETL/pen>
42 #include <ETL/misc>
43
44 #include "rectangle.h"
45
46 #endif
47
48 /* === U S I N G =========================================================== */
49
50 using namespace etl;
51 using namespace std;
52 using namespace synfig;
53
54 /* === G L O B A L S ======================================================= */
55
56 SYNFIG_LAYER_INIT(Rectangle);
57 SYNFIG_LAYER_SET_NAME(Rectangle,"rectangle");
58 SYNFIG_LAYER_SET_LOCAL_NAME(Rectangle,N_("Rectangle"));
59 SYNFIG_LAYER_SET_CATEGORY(Rectangle,N_("Geometry"));
60 SYNFIG_LAYER_SET_VERSION(Rectangle,"0.2");
61 SYNFIG_LAYER_SET_CVS_ID(Rectangle,"$Id$");
62
63 /* === P R O C E D U R E S ================================================= */
64
65 /*
66 inline int ceil_to_int(const float x) { return static_cast<int>(ceil(x)); }
67 inline int ceil_to_int(const double x) { return static_cast<int>(ceil(x)); }
68
69 inline int floor_to_int(const float x) { return static_cast<int>(floor(x)); }
70 inline int floor_to_int(const double x) { return static_cast<int>(floor(x)); }
71 */
72
73 /* === M E T H O D S ======================================================= */
74
75 /* === E N T R Y P O I N T ================================================= */
76
77 Rectangle::Rectangle():
78         Layer_Composite(1.0,Color::BLEND_COMPOSITE),
79         color(Color::black()),
80         point1(0,0),
81         point2(1,1),
82         expand(0),
83         invert(false)
84 {
85         Layer::Vocab voc(get_param_vocab());
86         Layer::fill_static(voc);
87 }
88
89 bool
90 Rectangle::set_param(const String & param, const ValueBase &value)
91 {
92         IMPORT_PLUS(color, { if (color.get_a() == 0) { if (converted_blend_) {
93                                         set_blend_method(Color::BLEND_ALPHA_OVER);
94                                         color.set_a(1); } else transparent_color_ = true; } });
95         IMPORT(point1);
96         IMPORT(point2);
97         IMPORT(expand);
98         IMPORT(invert);
99
100         return Layer_Composite::set_param(param,value);
101 }
102
103 ValueBase
104 Rectangle::get_param(const String &param)const
105 {
106         EXPORT(color);
107         EXPORT(point1);
108         EXPORT(point2);
109         EXPORT(expand);
110         EXPORT(invert);
111
112         EXPORT_NAME();
113         EXPORT_VERSION();
114
115         return Layer_Composite::get_param(param);
116 }
117
118 Layer::Vocab
119 Rectangle::get_param_vocab()const
120 {
121         Layer::Vocab ret(Layer_Composite::get_param_vocab());
122
123         ret.push_back(ParamDesc("color")
124                 .set_local_name(_("Color"))
125         );
126
127         ret.push_back(ParamDesc("point1")
128                 .set_local_name(_("Point 1"))
129                 .set_box("point2")
130         );
131
132         ret.push_back(ParamDesc("point2")
133                 .set_local_name(_("Point 2"))
134         );
135
136         ret.push_back(ParamDesc("expand")
137                 .set_is_distance()
138                 .set_local_name(_("Expand amount"))
139         );
140
141         ret.push_back(ParamDesc("invert")
142                 .set_local_name(_("Invert the rectangle"))
143         );
144
145         return ret;
146 }
147
148 synfig::Layer::Handle
149 Rectangle::hit_check(synfig::Context context, const synfig::Point &pos)const
150 {
151         if(is_disabled())
152                 return context.hit_check(pos);
153
154         Point max,min;
155
156         max[0]=std::max(point1[0],point2[0])+expand;
157         max[1]=std::max(point1[1],point2[1])+expand;
158         min[0]=std::min(point1[0],point2[0])-expand;
159         min[1]=std::min(point1[1],point2[1])-expand;
160
161         bool intersect(false);
162
163         if(     pos[0]<max[0] && pos[0]>min[0] &&
164                 pos[1]<max[1] && pos[1]>min[1] )
165         {
166                 intersect=true;
167         }
168
169         if(invert)
170                 intersect=!intersect;
171
172         if(intersect)
173         {
174                 synfig::Layer::Handle tmp;
175                 if(get_blend_method()==Color::BLEND_BEHIND && (tmp=context.hit_check(pos)))
176                         return tmp;
177                 if(Color::is_onto(get_blend_method()) && !(tmp=context.hit_check(pos)))
178                         return 0;
179                 return const_cast<Rectangle*>(this);
180         }
181
182         return context.hit_check(pos);
183 }
184
185 bool
186 Rectangle::is_solid_color()const
187 {
188         return Layer_Composite::is_solid_color() ||
189                 (get_blend_method() == Color::BLEND_COMPOSITE &&
190                  get_amount() == 1.0f &&
191                  color.get_a() == 1.0f);
192 }
193
194 Color
195 Rectangle::get_color(Context context, const Point &pos)const
196 {
197         if(is_disabled())
198                 return context.get_color(pos);
199
200         Point max,min;
201
202         max[0]=std::max(point1[0],point2[0])+expand;
203         max[1]=std::max(point1[1],point2[1])+expand;
204         min[0]=std::min(point1[0],point2[0])-expand;
205         min[1]=std::min(point1[1],point2[1])-expand;
206
207 /**************************
208 // This is darco's old-old-old feathered box code
209 // it produces really nice feathered edges
210         if(feather!=0.0)
211         {
212                 if(     pos[0]<=max[0]-feather/2.0 && pos[0]>=min[0]+feather/2.0 &&
213                         pos[1]<=max[1]-feather/2.0 && pos[1]>=min[1]+feather/2.0 )
214                 {
215                         if(invert)
216                                 return (*context).GetColor(context,pos);
217                         else
218                                 return color;
219                 }
220
221                 if(     pos[0]>=max[0]+feather/2.0 || pos[0]<=min[0]-feather/2.0 ||
222                         pos[1]>=max[1]+feather/2.0 || pos[1]<=min[1]-feather/2.0 )
223                 {
224                         if(invert)
225                                 return color;
226                         else
227                                 return (*context).GetColor(context,pos);
228                 }
229
230                 Color::unit alpha=1000000;
231                 Color::unit alpha2=1000000;
232
233                 if(max[0]-pos[0]+feather/2.0<alpha)
234                         alpha=max[0]-pos[0]+feather/2.0;
235                 if(pos[0]-min[0]+feather/2.0<alpha)
236                         alpha=pos[0]-min[0]+feather/2.0;
237
238                 if(max[1]-pos[1]+feather/2.0<alpha2)
239                         alpha2=max[1]-pos[1]+feather/2.0;
240                 if(pos[1]-min[1]+feather/2.0<alpha2)
241                         alpha2=pos[1]-min[1]+feather/2.0;
242
243
244                 if(alpha<=feather && alpha2<=feather)
245                 {
246                         alpha=feather-alpha;
247                         alpha2=feather-alpha2;
248
249                         alpha=sqrt(alpha*alpha+alpha2*alpha2);
250
251                         if(alpha>=feather)
252                         {
253                                 if(invert)
254                                         return color;
255                                 else
256                                         return (*context).GetColor(context,pos);
257                         }
258
259                         alpha=feather-alpha;
260                 }
261                 else
262                 {
263                         alpha=(alpha<alpha2)?alpha:alpha2;
264                 }
265
266                 alpha/=feather;
267
268                 if(invert)
269                         alpha=1.0-alpha;
270
271                 return Color::blend(color,context.get_color(pos),alpha,get_blend_method());
272         }
273
274 *****************/
275
276         if(     pos[0]<max[0] && pos[0]>min[0] &&
277                 pos[1]<max[1] && pos[1]>min[1] )
278         {
279                 // inside the expanded rectangle
280                 if(invert)
281                         return Color::blend(Color::alpha(),context.get_color(pos),get_amount(),get_blend_method());
282
283                 if(is_solid_color())
284                         return color;
285
286                 return Color::blend(color,context.get_color(pos),get_amount(),get_blend_method());
287         }
288         else
289         {
290                 // outside the expanded rectangle
291                 if(!invert)
292                         return Color::blend(Color::alpha(),context.get_color(pos),get_amount(),get_blend_method());
293
294                 if(is_solid_color())
295                         return color;
296
297                 return Color::blend(color,context.get_color(pos),get_amount(),get_blend_method());
298         }
299 }
300
301 bool
302 Rectangle::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
303 {
304         if(is_disabled())
305                 return context.accelerated_render(surface,quality,renddesc,cb);
306
307         const Point     tl(renddesc.get_tl());
308         const Point br(renddesc.get_br());
309
310         const int       w(renddesc.get_w());
311         const int       h(renddesc.get_h());
312
313         // Width and Height of a pixel
314         const Real pw = (br[0] - tl[0]) / w;
315         const Real ph = (br[1] - tl[1]) / h;
316
317         Point max(point1),min(point2);
318
319
320
321
322         /*
323
324         if(invert)
325         {
326                 max=context.get_bounding_rect().get_max();
327                 min=context.get_bounding_rect().get_min();
328         }
329         else
330         {
331                 max=context.get_full_bounding_rect().get_max();
332                 min=context.get_full_bounding_rect().get_min();
333         }
334         */
335
336
337
338
339
340         if((min[0] > max[0]) ^ (pw < 0))swap(min[0],max[0]);
341         if((min[1] > max[1]) ^ (ph < 0))swap(min[1],max[1]);
342
343         if(min[0] > max[0])
344         {
345                 min[0]+=expand;
346                 max[0]-=expand;
347         }
348         else
349         {
350                 min[0]-=expand;
351                 max[0]+=expand;
352         }
353
354         if(min[1] > max[1])
355         {
356                 min[1]+=expand;
357                 max[1]-=expand;
358         }
359         else
360         {
361                 min[1]-=expand;
362                 max[1]+=expand;
363         }
364
365         if(invert)
366         {
367                 int left(floor_to_int((min[0]-tl[0])/pw));
368                 int right(ceil_to_int((max[0]-tl[0])/pw));
369                 int top(floor_to_int((min[1]-tl[1])/ph));
370                 int bottom(ceil_to_int((max[1]-tl[1])/ph));
371
372                 float left_edge((min[0]-tl[0])/pw-float(left));
373                 float right_edge(float(right)-(max[0]-tl[0])/pw);
374                 float top_edge((min[1]-tl[1])/ph-float(top));
375                 float bottom_edge(float(bottom)-(max[1]-tl[1])/ph);
376
377                 if(top<0)top=0,top_edge=0;
378                 if(left<0)left=0,left_edge=0;
379                 if(bottom>h)bottom=h,bottom_edge=0;
380                 if(right>w)right=w,right_edge=0;
381
382                 if(is_solid_color())
383                 {
384                         Surface subimage;
385                         RendDesc desc(renddesc);
386                         desc.set_flags(0);
387
388                         //fill the surface with the background color initially
389                         surface->set_wh(w,h);
390                         surface->fill(color);
391
392                         // Check for the case where there is nothing to render
393                         if (right <= left || bottom <= top)
394                                 return true;
395
396                         desc.set_subwindow(left,top,right-left,bottom-top);
397
398                         // Render what is behind us
399                         if(!context.accelerated_render(&subimage,quality,desc,cb))
400                         {
401                                 if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
402                                 return false;
403                         }
404
405                         Surface::pen pen(surface->get_pen(left,top));
406
407                         subimage.blit_to(pen);
408                 }
409                 else
410                 {
411                         if(!context.accelerated_render(surface,quality,renddesc,cb))
412                         {
413                                 if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
414                                 return false;
415                         }
416
417                         Surface subimage;
418
419                         // Check for the case where there is something to render
420                         if (right > left && bottom > top)
421                         {
422                                 // save a copy of the overlapping region from surface into subimage
423                                 subimage.set_wh(right-left,bottom-top);
424                                 Surface::pen subimage_pen(subimage.begin());
425                                 surface->blit_to(subimage_pen,left,top,right-left,bottom-top);
426                         }
427
428                         // fill surface with the rectangle's color
429                         Surface::alpha_pen surface_pen(surface->begin(),get_amount(),get_blend_method());
430                         surface->fill(color,surface_pen,w,h);
431
432                         if (subimage)
433                         {
434                                 // copy the saved overlapping region back from subimage into surface
435                                 Surface::pen pen(surface->get_pen(left,top));
436                                 subimage.blit_to(pen);
437                         }
438                         else
439                                 // if there's no overlapping region, return now of the following code corrupts memory
440                                 return true;
441                 }
442
443                 Surface::alpha_pen pen;
444
445                 if(bottom-1>=0 && bottom_edge)
446                 {
447                         pen=Surface::alpha_pen(surface->get_pen(left,bottom-1),get_amount()*bottom_edge,get_blend_method());
448                         surface->fill(color,pen,right-left,1);
449                 }
450
451                 if(right-1>=0 && right_edge)
452                 {
453                         pen=Surface::alpha_pen(surface->get_pen(right-1,top),get_amount()*right_edge,get_blend_method());
454                         surface->fill(color,pen,1,bottom-top);
455                 }
456
457                 if(left>=0 && left_edge)
458                 {
459                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*left_edge,get_blend_method());
460                         surface->fill(color,pen,1,bottom-top);
461                 }
462
463                 if(top>=0 && top_edge)
464                 {
465                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*top_edge,get_blend_method());
466                         surface->fill(color,pen,right-left,1);
467                 }
468
469                 return true;
470         }
471
472         // not inverted
473
474         int left(ceil_to_int((min[0]-tl[0])/pw));
475         int right(floor_to_int((max[0]-tl[0])/pw));
476         int top(ceil_to_int((min[1]-tl[1])/ph));
477         int bottom(floor_to_int((max[1]-tl[1])/ph));
478
479         float left_edge(float(left)-(min[0]-tl[0])/pw);
480         float right_edge((max[0]-tl[0])/pw-float(right));
481         float top_edge(float(top)-(min[1]-tl[1])/ph);
482         float bottom_edge((max[1]-tl[1])/ph-float(bottom));
483
484         if(top<=0)top=0,top_edge=0;
485         if(left<=0)left=0,left_edge=0;
486         if(bottom>=h)bottom=h,bottom_edge=0;
487         if(right>=w)right=w,right_edge=0;
488
489 /*
490         top = std::max(0,top);
491         left = std::max(0,left);
492         bottom = std::min(h,bottom);
493         right = std::min(w,right);
494 */
495
496         // optimization - if the whole tile is covered by this rectangle,
497         // and the rectangle is a solid color, we don't need to render
498         // what's behind us
499         if (is_solid_color() && top == 0 && left == 0 && bottom == h && right == w)
500         {
501                 surface->set_wh(w,h);
502                 surface->fill(color);
503                 return true;
504         }
505
506         // Render what is behind us
507         if(!context.accelerated_render(surface,quality,renddesc,cb))
508         {
509                 if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
510                 return false;
511         }
512
513         // In the case where there is nothing to render...
514         if (right < left || bottom < top)
515                 return true;
516
517         Surface::alpha_pen pen;
518
519         if(right-left>0&&bottom-top>0)
520         {
521                 if(is_solid_color())
522                         surface->fill(color,left,top,right-left,bottom-top);
523                 else
524                 {
525                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount(),get_blend_method());
526                         surface->fill(color,pen,right-left,bottom-top);
527                 }
528         }
529
530         if(bottom<surface->get_h() && bottom_edge>=0.0001)
531         {
532                 pen=Surface::alpha_pen(surface->get_pen(left,bottom),get_amount()*bottom_edge,get_blend_method());
533                 surface->fill(color,pen,right-left,1);
534         }
535
536         if(right<surface->get_w() && right_edge>=0.0001)
537         {
538                 pen=Surface::alpha_pen(surface->get_pen(right,top),get_amount()*right_edge,get_blend_method());
539                 surface->fill(color,pen,1,bottom-top);
540         }
541
542         if(left>0 && left_edge>=0.0001)
543         {
544                 pen=Surface::alpha_pen(surface->get_pen(left-1,top),get_amount()*left_edge,get_blend_method());
545                 surface->fill(color,pen,1,bottom-top);
546         }
547
548         if(top>0 && top_edge>=0.0001)
549         {
550                 pen=Surface::alpha_pen(surface->get_pen(left,top-1),get_amount()*top_edge,get_blend_method());
551                 surface->fill(color,pen,right-left,1);
552         }
553
554
555         return true;
556 }
557
558 Rect
559 Rectangle::get_bounding_rect()const
560 {
561         if(invert)
562                 return Rect::full_plane();
563
564         Point max(point1),min(point2);
565         if((min[0] > max[0]))swap(min[0],max[0]);
566         if((min[1] > max[1]))swap(min[1],max[1]);
567         if(min[0] > max[0])
568         {
569                 min[0]+=expand;
570                 max[0]-=expand;
571         }
572         else
573         {
574                 min[0]-=expand;
575                 max[0]+=expand;
576         }
577
578         if(min[1] > max[1])
579         {
580                 min[1]+=expand;
581                 max[1]-=expand;
582         }
583         else
584         {
585                 min[1]-=expand;
586                 max[1]+=expand;
587         }
588
589         Rect bounds(min,max);
590
591         return bounds;
592 }
593
594 Rect
595 Rectangle::get_full_bounding_rect(Context context)const
596 {
597         if(invert)
598         {
599                 if(is_solid_color() && color.get_a()==0)
600                 {
601                         Point max(point1),min(point2);
602                         if((min[0] > max[0]))swap(min[0],max[0]);
603                         if((min[1] > max[1]))swap(min[1],max[1]);
604                         if(min[0] > max[0])
605                         {
606                                 min[0]+=expand;
607                                 max[0]-=expand;
608                         }
609                         else
610                         {
611                                 min[0]-=expand;
612                                 max[0]+=expand;
613                         }
614
615                         if(min[1] > max[1])
616                         {
617                                 min[1]+=expand;
618                                 max[1]-=expand;
619                         }
620                         else
621                         {
622                                 min[1]-=expand;
623                                 max[1]+=expand;
624                         }
625
626                         Rect bounds(min,max);
627
628                         return bounds & context.get_full_bounding_rect();
629                 }
630                 return Rect::full_plane();
631         }
632
633         return Layer_Composite::get_full_bounding_rect(context);
634 }