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