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