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