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