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