Fix 1689329: if the inverted rectangle we're rendering doesn't overlap the tile we...
[synfig.git] / synfig-core / trunk / 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 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                         else return true;
423                 }
424
425                 Surface::alpha_pen pen;
426
427                 if(bottom-1>=0 && bottom_edge)
428                 {
429                         pen=Surface::alpha_pen(surface->get_pen(left,bottom-1),get_amount()*bottom_edge,get_blend_method());
430                         surface->fill(color,pen,right-left,1);
431                 }
432
433                 if(right-1>=0 && right_edge)
434                 {
435                         pen=Surface::alpha_pen(surface->get_pen(right-1,top),get_amount()*right_edge,get_blend_method());
436                         surface->fill(color,pen,1,bottom-top);
437                 }
438
439                 if(left>=0 && left_edge)
440                 {
441                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*left_edge,get_blend_method());
442                         surface->fill(color,pen,1,bottom-top);
443                 }
444
445                 if(top>=0 && top_edge)
446                 {
447                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*top_edge,get_blend_method());
448                         surface->fill(color,pen,right-left,1);
449                 }
450
451                 return true;
452         }
453
454         // Render what is behind us
455         if(!context.accelerated_render(surface,quality,renddesc,cb))
456         {
457                 if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
458                 return false;
459         }
460
461         int left(ceil_to_int((min[0]-tl[0])/pw));
462         int right(floor_to_int((max[0]-tl[0])/pw));
463         int top(ceil_to_int((min[1]-tl[1])/ph));
464         int bottom(floor_to_int((max[1]-tl[1])/ph));
465
466         float left_edge(float(left)-(min[0]-tl[0])/pw);
467         float right_edge((max[0]-tl[0])/pw-float(right));
468         float top_edge(float(top)-(min[1]-tl[1])/ph);
469         float bottom_edge((max[1]-tl[1])/ph-float(bottom));
470
471         if(top<=0)top=0,top_edge=0;
472         if(left<=0)left=0,left_edge=0;
473         if(bottom>=h)bottom=h,bottom_edge=0;
474         if(right>=w)right=w,right_edge=0;
475
476 /*
477         top = std::max(0,top);
478         left = std::max(0,left);
479         bottom = std::min(h,bottom);
480         right = std::min(w,right);
481 */
482
483         Surface::alpha_pen pen;
484
485         // In the case where there is nothing to render...
486         if(right-left<0||bottom-top<0)
487                 return true;
488
489         if(right-left>0&&bottom-top>0)
490         {
491                 if(is_solid_color())
492                         surface->fill(color,left,top,right-left,bottom-top);
493                 else
494                 {
495                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount(),get_blend_method());
496                         surface->fill(color,pen,right-left,bottom-top);
497                 }
498         }
499
500         if(bottom<surface->get_h() && bottom_edge>=0.0001)
501         {
502                 pen=Surface::alpha_pen(surface->get_pen(left,bottom),get_amount()*bottom_edge,get_blend_method());
503                 surface->fill(color,pen,right-left,1);
504         }
505
506         if(right<surface->get_w() && right_edge>=0.0001)
507         {
508                 pen=Surface::alpha_pen(surface->get_pen(right,top),get_amount()*right_edge,get_blend_method());
509                 surface->fill(color,pen,1,bottom-top);
510         }
511
512         if(left>0 && left_edge>=0.0001)
513         {
514                 pen=Surface::alpha_pen(surface->get_pen(left-1,top),get_amount()*left_edge,get_blend_method());
515                 surface->fill(color,pen,1,bottom-top);
516         }
517
518         if(top>0 && top_edge>=0.0001)
519         {
520                 pen=Surface::alpha_pen(surface->get_pen(left,top-1),get_amount()*top_edge,get_blend_method());
521                 surface->fill(color,pen,right-left,1);
522         }
523
524
525         return true;
526 }
527
528 Rect
529 Rectangle::get_bounding_rect()const
530 {
531         if(invert)
532                 return Rect::full_plane();
533
534         Point max(point1),min(point2);
535         if((min[0] > max[0]))swap(min[0],max[0]);
536         if((min[1] > max[1]))swap(min[1],max[1]);
537         if(min[0] > max[0])
538         {
539                 min[0]+=expand;
540                 max[0]-=expand;
541         }
542         else
543         {
544                 min[0]-=expand;
545                 max[0]+=expand;
546         }
547
548         if(min[1] > max[1])
549         {
550                 min[1]+=expand;
551                 max[1]-=expand;
552         }
553         else
554         {
555                 min[1]-=expand;
556                 max[1]+=expand;
557         }
558
559         Rect bounds(min,max);
560
561         return bounds;
562 }
563
564 Rect
565 Rectangle::get_full_bounding_rect(Context context)const
566 {
567         if(invert)
568         {
569                 if(is_solid_color() && color.get_a()==0)
570                 {
571                         Point max(point1),min(point2);
572                         if((min[0] > max[0]))swap(min[0],max[0]);
573                         if((min[1] > max[1]))swap(min[1],max[1]);
574                         if(min[0] > max[0])
575                         {
576                                 min[0]+=expand;
577                                 max[0]-=expand;
578                         }
579                         else
580                         {
581                                 min[0]-=expand;
582                                 max[0]+=expand;
583                         }
584
585                         if(min[1] > max[1])
586                         {
587                                 min[1]+=expand;
588                                 max[1]-=expand;
589                         }
590                         else
591                         {
592                                 min[1]-=expand;
593                                 max[1]+=expand;
594                         }
595
596                         Rect bounds(min,max);
597
598                         return bounds & context.get_full_bounding_rect();
599                 }
600                 return Rect::full_plane();
601         }
602
603         return Layer_Composite::get_full_bounding_rect(context);
604 }