Don't waste time rendering layers which are completely obscured by solid-colour recta...
[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 || bottom <= top)
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
404                         Surface subimage;
405
406                         // Check for the case where there is something to render
407                         if (right > left && bottom > top)
408                         {
409                                 // save a copy of the overlapping region from surface into subimage
410                                 subimage.set_wh(right-left,bottom-top);
411                                 Surface::pen subimage_pen(subimage.begin());
412                                 surface->blit_to(subimage_pen,left,top,right-left,bottom-top);
413                         }
414
415                         // fill surface with the rectangle's colour
416                         Surface::alpha_pen surface_pen(surface->begin(),get_amount(),get_blend_method());
417                         surface->fill(color,surface_pen,w,h);
418
419                         if (subimage)
420                         {
421                                 // copy the saved overlapping region back from subimage into surface
422                                 Surface::pen pen(surface->get_pen(left,top));
423                                 subimage.blit_to(pen);
424                         }
425                         else
426                                 // if there's no overlapping region, return now of the following code corrupts memory
427                                 return true;
428                 }
429
430                 Surface::alpha_pen pen;
431
432                 if(bottom-1>=0 && bottom_edge)
433                 {
434                         pen=Surface::alpha_pen(surface->get_pen(left,bottom-1),get_amount()*bottom_edge,get_blend_method());
435                         surface->fill(color,pen,right-left,1);
436                 }
437
438                 if(right-1>=0 && right_edge)
439                 {
440                         pen=Surface::alpha_pen(surface->get_pen(right-1,top),get_amount()*right_edge,get_blend_method());
441                         surface->fill(color,pen,1,bottom-top);
442                 }
443
444                 if(left>=0 && left_edge)
445                 {
446                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*left_edge,get_blend_method());
447                         surface->fill(color,pen,1,bottom-top);
448                 }
449
450                 if(top>=0 && top_edge)
451                 {
452                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*top_edge,get_blend_method());
453                         surface->fill(color,pen,right-left,1);
454                 }
455
456                 return true;
457         }
458
459         // not inverted
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 || bottom < top)
487                 return true;
488
489         // optimisation - if the whole tile is covered by this rectangle,
490         // and the rectangle is a solid colour, we don't need to render
491         // what's behind us
492         if (is_solid_color() && top == 0 && left == 0 && bottom == h && right == w)
493         {
494                 surface->set_wh(w,h);
495                 surface->fill(color);
496                 return true;
497         }
498
499         // Render what is behind us
500         if(!context.accelerated_render(surface,quality,renddesc,cb))
501         {
502                 if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
503                 return false;
504         }
505
506         if(right-left>0&&bottom-top>0)
507         {
508                 if(is_solid_color())
509                         surface->fill(color,left,top,right-left,bottom-top);
510                 else
511                 {
512                         pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount(),get_blend_method());
513                         surface->fill(color,pen,right-left,bottom-top);
514                 }
515         }
516
517         if(bottom<surface->get_h() && bottom_edge>=0.0001)
518         {
519                 pen=Surface::alpha_pen(surface->get_pen(left,bottom),get_amount()*bottom_edge,get_blend_method());
520                 surface->fill(color,pen,right-left,1);
521         }
522
523         if(right<surface->get_w() && right_edge>=0.0001)
524         {
525                 pen=Surface::alpha_pen(surface->get_pen(right,top),get_amount()*right_edge,get_blend_method());
526                 surface->fill(color,pen,1,bottom-top);
527         }
528
529         if(left>0 && left_edge>=0.0001)
530         {
531                 pen=Surface::alpha_pen(surface->get_pen(left-1,top),get_amount()*left_edge,get_blend_method());
532                 surface->fill(color,pen,1,bottom-top);
533         }
534
535         if(top>0 && top_edge>=0.0001)
536         {
537                 pen=Surface::alpha_pen(surface->get_pen(left,top-1),get_amount()*top_edge,get_blend_method());
538                 surface->fill(color,pen,right-left,1);
539         }
540
541
542         return true;
543 }
544
545 Rect
546 Rectangle::get_bounding_rect()const
547 {
548         if(invert)
549                 return Rect::full_plane();
550
551         Point max(point1),min(point2);
552         if((min[0] > max[0]))swap(min[0],max[0]);
553         if((min[1] > max[1]))swap(min[1],max[1]);
554         if(min[0] > max[0])
555         {
556                 min[0]+=expand;
557                 max[0]-=expand;
558         }
559         else
560         {
561                 min[0]-=expand;
562                 max[0]+=expand;
563         }
564
565         if(min[1] > max[1])
566         {
567                 min[1]+=expand;
568                 max[1]-=expand;
569         }
570         else
571         {
572                 min[1]-=expand;
573                 max[1]+=expand;
574         }
575
576         Rect bounds(min,max);
577
578         return bounds;
579 }
580
581 Rect
582 Rectangle::get_full_bounding_rect(Context context)const
583 {
584         if(invert)
585         {
586                 if(is_solid_color() && color.get_a()==0)
587                 {
588                         Point max(point1),min(point2);
589                         if((min[0] > max[0]))swap(min[0],max[0]);
590                         if((min[1] > max[1]))swap(min[1],max[1]);
591                         if(min[0] > max[0])
592                         {
593                                 min[0]+=expand;
594                                 max[0]-=expand;
595                         }
596                         else
597                         {
598                                 min[0]-=expand;
599                                 max[0]+=expand;
600                         }
601
602                         if(min[1] > max[1])
603                         {
604                                 min[1]+=expand;
605                                 max[1]-=expand;
606                         }
607                         else
608                         {
609                                 min[1]-=expand;
610                                 max[1]+=expand;
611                         }
612
613                         Rect bounds(min,max);
614
615                         return bounds & context.get_full_bounding_rect();
616                 }
617                 return Rect::full_plane();
618         }
619
620         return Layer_Composite::get_full_bounding_rect(context);
621 }