More descriptions for layer parameters
[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                 .set_description(_("Fill color of the layer"))
126         );
127
128         ret.push_back(ParamDesc("point1")
129                 .set_local_name(_("Point 1"))
130                 .set_box("point2")
131                 .set_description(_("First corner of the rectangle"))
132         );
133
134         ret.push_back(ParamDesc("point2")
135                 .set_local_name(_("Point 2"))
136                 .set_description(_("Second corner of the rectangle"))
137         );
138
139         ret.push_back(ParamDesc("expand")
140                 .set_is_distance()
141                 .set_local_name(_("Expand amount"))
142         );
143
144         ret.push_back(ParamDesc("invert")
145                 .set_local_name(_("Invert the rectangle"))
146         );
147
148         return ret;
149 }
150
151 synfig::Layer::Handle
152 Rectangle::hit_check(synfig::Context context, const synfig::Point &pos)const
153 {
154         if(is_disabled())
155                 return context.hit_check(pos);
156
157         Point max,min;
158
159         max[0]=std::max(point1[0],point2[0])+expand;
160         max[1]=std::max(point1[1],point2[1])+expand;
161         min[0]=std::min(point1[0],point2[0])-expand;
162         min[1]=std::min(point1[1],point2[1])-expand;
163
164         bool intersect(false);
165
166         if(     pos[0]<max[0] && pos[0]>min[0] &&
167                 pos[1]<max[1] && pos[1]>min[1] )
168         {
169                 intersect=true;
170         }
171
172         if(invert)
173                 intersect=!intersect;
174
175         if(intersect)
176         {
177                 synfig::Layer::Handle tmp;
178                 if(get_blend_method()==Color::BLEND_BEHIND && (tmp=context.hit_check(pos)))
179                         return tmp;
180                 if(Color::is_onto(get_blend_method()) && !(tmp=context.hit_check(pos)))
181                         return 0;
182                 return const_cast<Rectangle*>(this);
183         }
184
185         return context.hit_check(pos);
186 }
187
188 bool
189 Rectangle::is_solid_color()const
190 {
191         return Layer_Composite::is_solid_color() ||
192                 (get_blend_method() == Color::BLEND_COMPOSITE &&
193                  get_amount() == 1.0f &&
194                  color.get_a() == 1.0f);
195 }
196
197 Color
198 Rectangle::get_color(Context context, const Point &pos)const
199 {
200         if(is_disabled())
201                 return context.get_color(pos);
202
203         Point max,min;
204
205         max[0]=std::max(point1[0],point2[0])+expand;
206         max[1]=std::max(point1[1],point2[1])+expand;
207         min[0]=std::min(point1[0],point2[0])-expand;
208         min[1]=std::min(point1[1],point2[1])-expand;
209
210 /**************************
211 // This is darco's old-old-old feathered box code
212 // it produces really nice feathered edges
213         if(feather!=0.0)
214         {
215                 if(     pos[0]<=max[0]-feather/2.0 && pos[0]>=min[0]+feather/2.0 &&
216                         pos[1]<=max[1]-feather/2.0 && pos[1]>=min[1]+feather/2.0 )
217                 {
218                         if(invert)
219                                 return (*context).GetColor(context,pos);
220                         else
221                                 return color;
222                 }
223
224                 if(     pos[0]>=max[0]+feather/2.0 || pos[0]<=min[0]-feather/2.0 ||
225                         pos[1]>=max[1]+feather/2.0 || pos[1]<=min[1]-feather/2.0 )
226                 {
227                         if(invert)
228                                 return color;
229                         else
230                                 return (*context).GetColor(context,pos);
231                 }
232
233                 Color::unit alpha=1000000;
234                 Color::unit alpha2=1000000;
235
236                 if(max[0]-pos[0]+feather/2.0<alpha)
237                         alpha=max[0]-pos[0]+feather/2.0;
238                 if(pos[0]-min[0]+feather/2.0<alpha)
239                         alpha=pos[0]-min[0]+feather/2.0;
240
241                 if(max[1]-pos[1]+feather/2.0<alpha2)
242                         alpha2=max[1]-pos[1]+feather/2.0;
243                 if(pos[1]-min[1]+feather/2.0<alpha2)
244                         alpha2=pos[1]-min[1]+feather/2.0;
245
246
247                 if(alpha<=feather && alpha2<=feather)
248                 {
249                         alpha=feather-alpha;
250                         alpha2=feather-alpha2;
251
252                         alpha=sqrt(alpha*alpha+alpha2*alpha2);
253
254                         if(alpha>=feather)
255                         {
256                                 if(invert)
257                                         return color;
258                                 else
259                                         return (*context).GetColor(context,pos);
260                         }
261
262                         alpha=feather-alpha;
263                 }
264                 else
265                 {
266                         alpha=(alpha<alpha2)?alpha:alpha2;
267                 }
268
269                 alpha/=feather;
270
271                 if(invert)
272                         alpha=1.0-alpha;
273
274                 return Color::blend(color,context.get_color(pos),alpha,get_blend_method());
275         }
276
277 *****************/
278
279         if(     pos[0]<max[0] && pos[0]>min[0] &&
280                 pos[1]<max[1] && pos[1]>min[1] )
281         {
282                 // inside the expanded rectangle
283                 if(invert)
284                         return Color::blend(Color::alpha(),context.get_color(pos),get_amount(),get_blend_method());
285
286                 if(is_solid_color())
287                         return color;
288
289                 return Color::blend(color,context.get_color(pos),get_amount(),get_blend_method());
290         }
291         else
292         {
293                 // outside the expanded rectangle
294                 if(!invert)
295                         return Color::blend(Color::alpha(),context.get_color(pos),get_amount(),get_blend_method());
296
297                 if(is_solid_color())
298                         return color;
299
300                 return Color::blend(color,context.get_color(pos),get_amount(),get_blend_method());
301         }
302 }
303
304 bool
305 Rectangle::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
306 {
307         if(is_disabled())
308                 return context.accelerated_render(surface,quality,renddesc,cb);
309
310         const Point     tl(renddesc.get_tl());
311         const Point br(renddesc.get_br());
312
313         const int       w(renddesc.get_w());
314         const int       h(renddesc.get_h());
315
316         // Width and Height of a pixel
317         const Real pw = (br[0] - tl[0]) / w;
318         const Real ph = (br[1] - tl[1]) / h;
319
320         Point max(point1),min(point2);
321
322
323
324
325         /*
326
327         if(invert)
328         {
329                 max=context.get_bounding_rect().get_max();
330                 min=context.get_bounding_rect().get_min();
331         }
332         else
333         {
334                 max=context.get_full_bounding_rect().get_max();
335                 min=context.get_full_bounding_rect().get_min();
336         }
337         */
338
339
340
341
342
343         if((min[0] > max[0]) ^ (pw < 0))swap(min[0],max[0]);
344         if((min[1] > max[1]) ^ (ph < 0))swap(min[1],max[1]);
345
346         if(min[0] > max[0])
347         {
348                 min[0]+=expand;
349                 max[0]-=expand;
350         }
351         else
352         {
353                 min[0]-=expand;
354                 max[0]+=expand;
355         }
356
357         if(min[1] > max[1])
358         {
359                 min[1]+=expand;
360                 max[1]-=expand;
361         }
362         else
363         {
364                 min[1]-=expand;
365                 max[1]+=expand;
366         }
367
368         if(invert)
369         {
370                 int left(floor_to_int((min[0]-tl[0])/pw));
371                 int right(ceil_to_int((max[0]-tl[0])/pw));
372                 int top(floor_to_int((min[1]-tl[1])/ph));
373                 int bottom(ceil_to_int((max[1]-tl[1])/ph));
374
375                 float left_edge((min[0]-tl[0])/pw-float(left));
376                 float right_edge(float(right)-(max[0]-tl[0])/pw);
377                 float top_edge((min[1]-tl[1])/ph-float(top));
378                 float bottom_edge(float(bottom)-(max[1]-tl[1])/ph);
379
380                 if(top<0)top=0,top_edge=0;
381                 if(left<0)left=0,left_edge=0;
382                 if(bottom>h)bottom=h,bottom_edge=0;
383                 if(right>w)right=w,right_edge=0;
384
385                 if(is_solid_color())
386                 {
387                         Surface subimage;
388                         RendDesc desc(renddesc);
389                         desc.set_flags(0);
390
391                         //fill the surface with the background color initially
392                         surface->set_wh(w,h);
393                         surface->fill(color);
394
395                         // Check for the case where there is nothing to render
396                         if (right <= left || bottom <= top)
397                                 return true;
398
399                         desc.set_subwindow(left,top,right-left,bottom-top);
400
401                         // Render what is behind us
402                         if(!context.accelerated_render(&subimage,quality,desc,cb))
403                         {
404                                 if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
405                                 return false;
406                         }
407
408                         Surface::pen pen(surface->get_pen(left,top));
409
410                         subimage.blit_to(pen);
411                 }
412                 else
413                 {
414                         if(!context.accelerated_render(surface,quality,renddesc,cb))
415                         {
416                                 if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
417                                 return false;
418                         }
419
420                         Surface subimage;
421
422                         // Check for the case where there is something to render
423                         if (right > left && bottom > top)
424                         {
425                                 // save a copy of the overlapping region from surface into subimage
426                                 subimage.set_wh(right-left,bottom-top);
427                                 Surface::pen subimage_pen(subimage.begin());
428                                 surface->blit_to(subimage_pen,left,top,right-left,bottom-top);
429                         }
430
431                         // fill surface with the rectangle's color
432                         Surface::alpha_pen surface_pen(surface->begin(),get_amount(),get_blend_method());
433                         surface->fill(color,surface_pen,w,h);
434
435                         if (subimage)
436                         {
437                                 // copy the saved overlapping region back from subimage into surface
438                                 Surface::pen pen(surface->get_pen(left,top));
439                                 subimage.blit_to(pen);
440                         }
441                         else
442                                 // if there's no overlapping region, return now of the following code corrupts memory
443                                 return true;
444                 }
445
446                 Surface::alpha_pen pen;
447
448                 if(bottom-1>=0 && bottom_edge)
449                 {
450                         pen=Surface::alpha_pen(surface->get_pen(left,bottom-1),get_amount()*bottom_edge,get_blend_method());
451                         surface->fill(color,pen,right-left,1);
452                 }
453
454                 if(right-1>=0 && right_edge)
455                 {
456                         pen=Surface::alpha_pen(surface->get_pen(right-1,top),get_amount()*right_edge,get_blend_method());
457                         surface->fill(color,pen,1,bottom-top);
458                 }
459
460                 if(left>=0 && left_edge)
461                 {
462                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*left_edge,get_blend_method());
463                         surface->fill(color,pen,1,bottom-top);
464                 }
465
466                 if(top>=0 && top_edge)
467                 {
468                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*top_edge,get_blend_method());
469                         surface->fill(color,pen,right-left,1);
470                 }
471
472                 return true;
473         }
474
475         // not inverted
476
477         int left(ceil_to_int((min[0]-tl[0])/pw));
478         int right(floor_to_int((max[0]-tl[0])/pw));
479         int top(ceil_to_int((min[1]-tl[1])/ph));
480         int bottom(floor_to_int((max[1]-tl[1])/ph));
481
482         float left_edge(float(left)-(min[0]-tl[0])/pw);
483         float right_edge((max[0]-tl[0])/pw-float(right));
484         float top_edge(float(top)-(min[1]-tl[1])/ph);
485         float bottom_edge((max[1]-tl[1])/ph-float(bottom));
486
487         if(top<=0)top=0,top_edge=0;
488         if(left<=0)left=0,left_edge=0;
489         if(bottom>=h)bottom=h,bottom_edge=0;
490         if(right>=w)right=w,right_edge=0;
491
492 /*
493         top = std::max(0,top);
494         left = std::max(0,left);
495         bottom = std::min(h,bottom);
496         right = std::min(w,right);
497 */
498
499         // optimization - if the whole tile is covered by this rectangle,
500         // and the rectangle is a solid color, we don't need to render
501         // what's behind us
502         if (is_solid_color() && top == 0 && left == 0 && bottom == h && right == w)
503         {
504                 surface->set_wh(w,h);
505                 surface->fill(color);
506                 return true;
507         }
508
509         // Render what is behind us
510         if(!context.accelerated_render(surface,quality,renddesc,cb))
511         {
512                 if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
513                 return false;
514         }
515
516         // In the case where there is nothing to render...
517         if (right < left || bottom < top)
518                 return true;
519
520         Surface::alpha_pen pen;
521
522         if(right-left>0&&bottom-top>0)
523         {
524                 if(is_solid_color())
525                         surface->fill(color,left,top,right-left,bottom-top);
526                 else
527                 {
528                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount(),get_blend_method());
529                         surface->fill(color,pen,right-left,bottom-top);
530                 }
531         }
532
533         if(bottom<surface->get_h() && bottom_edge>=0.0001)
534         {
535                 pen=Surface::alpha_pen(surface->get_pen(left,bottom),get_amount()*bottom_edge,get_blend_method());
536                 surface->fill(color,pen,right-left,1);
537         }
538
539         if(right<surface->get_w() && right_edge>=0.0001)
540         {
541                 pen=Surface::alpha_pen(surface->get_pen(right,top),get_amount()*right_edge,get_blend_method());
542                 surface->fill(color,pen,1,bottom-top);
543         }
544
545         if(left>0 && left_edge>=0.0001)
546         {
547                 pen=Surface::alpha_pen(surface->get_pen(left-1,top),get_amount()*left_edge,get_blend_method());
548                 surface->fill(color,pen,1,bottom-top);
549         }
550
551         if(top>0 && top_edge>=0.0001)
552         {
553                 pen=Surface::alpha_pen(surface->get_pen(left,top-1),get_amount()*top_edge,get_blend_method());
554                 surface->fill(color,pen,right-left,1);
555         }
556
557
558         return true;
559 }
560
561 Rect
562 Rectangle::get_bounding_rect()const
563 {
564         if(invert)
565                 return Rect::full_plane();
566
567         Point max(point1),min(point2);
568         if((min[0] > max[0]))swap(min[0],max[0]);
569         if((min[1] > max[1]))swap(min[1],max[1]);
570         if(min[0] > max[0])
571         {
572                 min[0]+=expand;
573                 max[0]-=expand;
574         }
575         else
576         {
577                 min[0]-=expand;
578                 max[0]+=expand;
579         }
580
581         if(min[1] > max[1])
582         {
583                 min[1]+=expand;
584                 max[1]-=expand;
585         }
586         else
587         {
588                 min[1]-=expand;
589                 max[1]+=expand;
590         }
591
592         Rect bounds(min,max);
593
594         return bounds;
595 }
596
597 Rect
598 Rectangle::get_full_bounding_rect(Context context)const
599 {
600         if(invert)
601         {
602                 if(is_solid_color() && color.get_a()==0)
603                 {
604                         Point max(point1),min(point2);
605                         if((min[0] > max[0]))swap(min[0],max[0]);
606                         if((min[1] > max[1]))swap(min[1],max[1]);
607                         if(min[0] > max[0])
608                         {
609                                 min[0]+=expand;
610                                 max[0]-=expand;
611                         }
612                         else
613                         {
614                                 min[0]-=expand;
615                                 max[0]+=expand;
616                         }
617
618                         if(min[1] > max[1])
619                         {
620                                 min[1]+=expand;
621                                 max[1]-=expand;
622                         }
623                         else
624                         {
625                                 min[1]-=expand;
626                                 max[1]+=expand;
627                         }
628
629                         Rect bounds(min,max);
630
631                         return bounds & context.get_full_bounding_rect();
632                 }
633                 return Rect::full_plane();
634         }
635
636         return Layer_Composite::get_full_bounding_rect(context);
637 }