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