Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / stable / src / modules / mod_geometry / rectangle.cpp
index 326b44e..374af9c 100644 (file)
@@ -1,20 +1,22 @@
-/* === S I N F G =========================================================== */
+/* === S Y N F I G ========================================================= */
 /*!    \file rectangle.cpp
-**     \point2ief Template Header
+**     \brief Implementation of the "Rectangle" layer
 **
-**     $Id: rectangle.cpp,v 1.2 2005/01/24 03:08:17 darco Exp $
+**     $Id$
 **
 **     \legal
-**     Copyright (c) 2002 Robert B. Quatpoint1ebaum Jr.
+**     Copyright (c) 2002 Robert B. Quattlebaum Jr.
+**     Copyright (c) 2007, 2008 Chris Moore
 **
-**     This software and associated documentation
-**     are CONFIDENTIAL and PROPRIETARY property of
-**     the above-mentioned copyright holder.
+**     This package is free software; you can redistribute it and/or
+**     modify it under the terms of the GNU General Public License as
+**     published by the Free Software Foundation; either version 2 of
+**     the License, or (at your option) any later version.
 **
-**     You may not copy, print, publish, or in any
-**     other way distribute this software without
-**     a prior written agreement with
-**     the copyright holder.
+**     This package is distributed in the hope that it will be useful,
+**     but WITHOUT ANY WARRANTY; without even the implied warranty of
+**     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+**     General Public License for more details.
 **     \endlegal
 */
 /* ========================================================================= */
 #      include <config.h>
 #endif
 
-#include <sinfg/string.h>
-#include <sinfg/time.h>
-#include <sinfg/context.h>
-#include <sinfg/paramdesc.h>
-#include <sinfg/renddesc.h>
-#include <sinfg/surface.h>
-#include <sinfg/value.h>
-#include <sinfg/valuenode.h>
+#include <synfig/string.h>
+#include <synfig/time.h>
+#include <synfig/context.h>
+#include <synfig/paramdesc.h>
+#include <synfig/renddesc.h>
+#include <synfig/surface.h>
+#include <synfig/value.h>
+#include <synfig/valuenode.h>
 #include <ETL/pen>
 #include <ETL/misc>
 
 
 using namespace etl;
 using namespace std;
-using namespace sinfg;
+using namespace synfig;
 
 /* === G L O B A L S ======================================================= */
 
-SINFG_LAYER_INIT(Rectangle);
-SINFG_LAYER_SET_NAME(Rectangle,"rectangle");
-SINFG_LAYER_SET_LOCAL_NAME(Rectangle,_("Rectangle"));
-SINFG_LAYER_SET_CATEGORY(Rectangle,_("Geometry"));
-SINFG_LAYER_SET_VERSION(Rectangle,"0.2");
-SINFG_LAYER_SET_CVS_ID(Rectangle,"$Id: rectangle.cpp,v 1.2 2005/01/24 03:08:17 darco Exp $");
+SYNFIG_LAYER_INIT(Rectangle);
+SYNFIG_LAYER_SET_NAME(Rectangle,"rectangle");
+SYNFIG_LAYER_SET_LOCAL_NAME(Rectangle,N_("Rectangle"));
+SYNFIG_LAYER_SET_CATEGORY(Rectangle,N_("Geometry"));
+SYNFIG_LAYER_SET_VERSION(Rectangle,"0.2");
+SYNFIG_LAYER_SET_CVS_ID(Rectangle,"$Id$");
 
 /* === P R O C E D U R E S ================================================= */
 
@@ -81,16 +83,18 @@ Rectangle::Rectangle():
        invert(false)
 {
 }
-       
+
 bool
 Rectangle::set_param(const String & param, const ValueBase &value)
 {
-       IMPORT(color);
+       IMPORT_PLUS(color, { if (color.get_a() == 0) { if (converted_blend_) {
+                                       set_blend_method(Color::BLEND_ALPHA_OVER);
+                                       color.set_a(1); } else transparent_color_ = true; } });
        IMPORT(point1);
        IMPORT(point2);
        IMPORT(expand);
        IMPORT(invert);
-       
+
        return Layer_Composite::set_param(param,value);
 }
 
@@ -105,15 +109,15 @@ Rectangle::get_param(const String &param)const
 
        EXPORT_NAME();
        EXPORT_VERSION();
-               
-       return Layer_Composite::get_param(param);       
+
+       return Layer_Composite::get_param(param);
 }
 
 Layer::Vocab
 Rectangle::get_param_vocab()const
 {
        Layer::Vocab ret(Layer_Composite::get_param_vocab());
-       
+
        ret.push_back(ParamDesc("color")
                .set_local_name(_("Color"))
        );
@@ -122,7 +126,7 @@ Rectangle::get_param_vocab()const
                .set_local_name(_("Point 1"))
                .set_box("point2")
        );
-       
+
        ret.push_back(ParamDesc("point2")
                .set_local_name(_("Point 2"))
        );
@@ -131,7 +135,7 @@ Rectangle::get_param_vocab()const
                .set_is_distance()
                .set_local_name(_("Expand amount"))
        );
-       
+
        ret.push_back(ParamDesc("invert")
                .set_local_name(_("Invert the rectangle"))
        );
@@ -139,8 +143,8 @@ Rectangle::get_param_vocab()const
        return ret;
 }
 
-sinfg::Layer::Handle
-Rectangle::hit_check(sinfg::Context context, const sinfg::Point &pos)const
+synfig::Layer::Handle
+Rectangle::hit_check(synfig::Context context, const synfig::Point &pos)const
 {
        if(is_disabled())
                return context.hit_check(pos);
@@ -153,7 +157,7 @@ Rectangle::hit_check(sinfg::Context context, const sinfg::Point &pos)const
        min[1]=std::min(point1[1],point2[1])-expand;
 
        bool intersect(false);
-       
+
        if(     pos[0]<max[0] && pos[0]>min[0] &&
                pos[1]<max[1] && pos[1]>min[1] )
        {
@@ -162,10 +166,10 @@ Rectangle::hit_check(sinfg::Context context, const sinfg::Point &pos)const
 
        if(invert)
                intersect=!intersect;
-       
+
        if(intersect)
        {
-               sinfg::Layer::Handle tmp;
+               synfig::Layer::Handle tmp;
                if(get_blend_method()==Color::BLEND_BEHIND && (tmp=context.hit_check(pos)))
                        return tmp;
                if(Color::is_onto(get_blend_method()) && !(tmp=context.hit_check(pos)))
@@ -176,6 +180,15 @@ Rectangle::hit_check(sinfg::Context context, const sinfg::Point &pos)const
        return context.hit_check(pos);
 }
 
+bool
+Rectangle::is_solid_color()const
+{
+       return Layer_Composite::is_solid_color() ||
+               (get_blend_method() == Color::BLEND_COMPOSITE &&
+                get_amount() == 1.0f &&
+                color.get_a() == 1.0f);
+}
+
 Color
 Rectangle::get_color(Context context, const Point &pos)const
 {
@@ -188,10 +201,10 @@ Rectangle::get_color(Context context, const Point &pos)const
        max[1]=std::max(point1[1],point2[1])+expand;
        min[0]=std::min(point1[0],point2[0])-expand;
        min[1]=std::min(point1[1],point2[1])-expand;
-       
+
 /**************************
 // This is darco's old-old-old feathered box code
-// it produces really nice feathered edges     
+// it produces really nice feathered edges
        if(feather!=0.0)
        {
                if(     pos[0]<=max[0]-feather/2.0 && pos[0]>=min[0]+feather/2.0 &&
@@ -214,12 +227,12 @@ Rectangle::get_color(Context context, const Point &pos)const
 
                Color::unit alpha=1000000;
                Color::unit alpha2=1000000;
-               
+
                if(max[0]-pos[0]+feather/2.0<alpha)
                        alpha=max[0]-pos[0]+feather/2.0;
                if(pos[0]-min[0]+feather/2.0<alpha)
                        alpha=pos[0]-min[0]+feather/2.0;
-               
+
                if(max[1]-pos[1]+feather/2.0<alpha2)
                        alpha2=max[1]-pos[1]+feather/2.0;
                if(pos[1]-min[1]+feather/2.0<alpha2)
@@ -232,7 +245,7 @@ Rectangle::get_color(Context context, const Point &pos)const
                        alpha2=feather-alpha2;
 
                        alpha=sqrt(alpha*alpha+alpha2*alpha2);
-                       
+
                        if(alpha>=feather)
                        {
                                if(invert)
@@ -255,38 +268,37 @@ Rectangle::get_color(Context context, const Point &pos)const
 
                return Color::blend(color,context.get_color(pos),alpha,get_blend_method());
        }
-       
+
 *****************/
 
        if(     pos[0]<max[0] && pos[0]>min[0] &&
                pos[1]<max[1] && pos[1]>min[1] )
        {
+               // inside the expanded rectangle
                if(invert)
-                       return context.get_color(pos);
-               else
-               {
-                       if(is_solid_color())
-                               return color;
-                       else
-                               return Color::blend(color,context.get_color(pos),get_amount(),get_blend_method());
-                               
-               }
-       }
+                       return Color::blend(Color::alpha(),context.get_color(pos),get_amount(),get_blend_method());
 
-       if(invert)
-       {
                if(is_solid_color())
                        return color;
-               else
-                       return Color::blend(color,context.get_color(pos),get_amount(),get_blend_method());
+
+               return Color::blend(color,context.get_color(pos),get_amount(),get_blend_method());
        }
+       else
+       {
+               // outside the expanded rectangle
+               if(!invert)
+                       return Color::blend(Color::alpha(),context.get_color(pos),get_amount(),get_blend_method());
 
-       return context.get_color(pos);
+               if(is_solid_color())
+                       return color;
+
+               return Color::blend(color,context.get_color(pos),get_amount(),get_blend_method());
+       }
 }
 
 bool
 Rectangle::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
-{      
+{
        if(is_disabled())
                return context.accelerated_render(surface,quality,renddesc,cb);
 
@@ -301,12 +313,12 @@ Rectangle::accelerated_render(Context context,Surface *surface,int quality, cons
        const Real ph = (br[1] - tl[1]) / h;
 
        Point max(point1),min(point2);
-       
-       
-       
-       
+
+
+
+
        /*
-       
+
        if(invert)
        {
                max=context.get_bounding_rect().get_max();
@@ -318,14 +330,14 @@ Rectangle::accelerated_render(Context context,Surface *surface,int quality, cons
                min=context.get_full_bounding_rect().get_min();
        }
        */
-       
-       
-       
-       
-       
+
+
+
+
+
        if((min[0] > max[0]) ^ (pw < 0))swap(min[0],max[0]);
        if((min[1] > max[1]) ^ (ph < 0))swap(min[1],max[1]);
-       
+
        if(min[0] > max[0])
        {
                min[0]+=expand;
@@ -347,49 +359,49 @@ Rectangle::accelerated_render(Context context,Surface *surface,int quality, cons
                min[1]-=expand;
                max[1]+=expand;
        }
-               
+
        if(invert)
        {
                int left(floor_to_int((min[0]-tl[0])/pw));
                int right(ceil_to_int((max[0]-tl[0])/pw));
                int top(floor_to_int((min[1]-tl[1])/ph));
                int bottom(ceil_to_int((max[1]-tl[1])/ph));
-       
+
                float left_edge((min[0]-tl[0])/pw-float(left));
                float right_edge(float(right)-(max[0]-tl[0])/pw);
                float top_edge((min[1]-tl[1])/ph-float(top));
                float bottom_edge(float(bottom)-(max[1]-tl[1])/ph);
-                       
+
                if(top<0)top=0,top_edge=0;
                if(left<0)left=0,left_edge=0;
                if(bottom>h)bottom=h,bottom_edge=0;
                if(right>w)right=w,right_edge=0;
-                       
+
                if(is_solid_color())
                {
                        Surface subimage;
                        RendDesc desc(renddesc);
                        desc.set_flags(0);
-                                       
+
                        //fill the surface with the background color initially
                        surface->set_wh(w,h);
                        surface->fill(color);
-       
+
                        // Check for the case where there is nothing to render
-                       if(right-left<=0||bottom-top<=0)
+                       if (right <= left || bottom <= top)
                                return true;
-                       
+
                        desc.set_subwindow(left,top,right-left,bottom-top);
-       
+
                        // Render what is behind us
                        if(!context.accelerated_render(&subimage,quality,desc,cb))
                        {
                                if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
                                return false;
                        }
-                       
+
                        Surface::pen pen(surface->get_pen(left,top));
-                       
+
                        subimage.blit_to(pen);
                }
                else
@@ -399,25 +411,31 @@ Rectangle::accelerated_render(Context context,Surface *surface,int quality, cons
                                if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
                                return false;
                        }
+
                        Surface subimage;
 
-                       // Check for the case where there is nothing to render
-                       if(!(right-left<=0||bottom-top<=0))
+                       // Check for the case where there is something to render
+                       if (right > left && bottom > top)
                        {
+                               // save a copy of the overlapping region from surface into subimage
                                subimage.set_wh(right-left,bottom-top);
                                Surface::pen subimage_pen(subimage.begin());
                                surface->blit_to(subimage_pen,left,top,right-left,bottom-top);
                        }
 
+                       // fill surface with the rectangle's color
                        Surface::alpha_pen surface_pen(surface->begin(),get_amount(),get_blend_method());
-               
                        surface->fill(color,surface_pen,w,h);
 
-                       if(subimage)
+                       if (subimage)
                        {
+                               // copy the saved overlapping region back from subimage into surface
                                Surface::pen pen(surface->get_pen(left,top));
                                subimage.blit_to(pen);
                        }
+                       else
+                               // if there's no overlapping region, return now of the following code corrupts memory
+                               return true;
                }
 
                Surface::alpha_pen pen;
@@ -427,34 +445,29 @@ Rectangle::accelerated_render(Context context,Surface *surface,int quality, cons
                        pen=Surface::alpha_pen(surface->get_pen(left,bottom-1),get_amount()*bottom_edge,get_blend_method());
                        surface->fill(color,pen,right-left,1);
                }
-               
+
                if(right-1>=0 && right_edge)
                {
                        pen=Surface::alpha_pen(surface->get_pen(right-1,top),get_amount()*right_edge,get_blend_method());
                        surface->fill(color,pen,1,bottom-top);
                }
-               
+
                if(left>=0 && left_edge)
                {
                        pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*left_edge,get_blend_method());
                        surface->fill(color,pen,1,bottom-top);
                }
-               
+
                if(top>=0 && top_edge)
                {
                        pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount()*top_edge,get_blend_method());
                        surface->fill(color,pen,right-left,1);
                }
-               
+
                return true;
        }
 
-       // Render what is behind us
-       if(!context.accelerated_render(surface,quality,renddesc,cb))
-       {
-               if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
-               return false;
-       }
+       // not inverted
 
        int left(ceil_to_int((min[0]-tl[0])/pw));
        int right(floor_to_int((max[0]-tl[0])/pw));
@@ -465,7 +478,7 @@ Rectangle::accelerated_render(Context context,Surface *surface,int quality, cons
        float right_edge((max[0]-tl[0])/pw-float(right));
        float top_edge(float(top)-(min[1]-tl[1])/ph);
        float bottom_edge((max[1]-tl[1])/ph-float(bottom));
-               
+
        if(top<=0)top=0,top_edge=0;
        if(left<=0)left=0,left_edge=0;
        if(bottom>=h)bottom=h,bottom_edge=0;
@@ -477,24 +490,41 @@ Rectangle::accelerated_render(Context context,Surface *surface,int quality, cons
        bottom = std::min(h,bottom);
        right = std::min(w,right);
 */
-       
-       Surface::alpha_pen pen;
+
+       // optimization - if the whole tile is covered by this rectangle,
+       // and the rectangle is a solid color, we don't need to render
+       // what's behind us
+       if (is_solid_color() && top == 0 && left == 0 && bottom == h && right == w)
+       {
+               surface->set_wh(w,h);
+               surface->fill(color);
+               return true;
+       }
+
+       // Render what is behind us
+       if(!context.accelerated_render(surface,quality,renddesc,cb))
+       {
+               if(cb)cb->error(strprintf(__FILE__"%d: Accelerated Renderer Failure",__LINE__));
+               return false;
+       }
 
        // In the case where there is nothing to render...
-       if(right-left<0||bottom-top<0)
+       if (right < left || bottom < top)
                return true;
-       
+
+       Surface::alpha_pen pen;
+
        if(right-left>0&&bottom-top>0)
        {
                if(is_solid_color())
                        surface->fill(color,left,top,right-left,bottom-top);
                else
-               {       
+               {
                        pen=Surface::alpha_pen(surface->get_pen(left,top),get_amount(),get_blend_method());
                        surface->fill(color,pen,right-left,bottom-top);
                }
        }
-       
+
        if(bottom<surface->get_h() && bottom_edge>=0.0001)
        {
                pen=Surface::alpha_pen(surface->get_pen(left,bottom),get_amount()*bottom_edge,get_blend_method());
@@ -506,7 +536,7 @@ Rectangle::accelerated_render(Context context,Surface *surface,int quality, cons
                pen=Surface::alpha_pen(surface->get_pen(right,top),get_amount()*right_edge,get_blend_method());
                surface->fill(color,pen,1,bottom-top);
        }
-       
+
        if(left>0 && left_edge>=0.0001)
        {
                pen=Surface::alpha_pen(surface->get_pen(left-1,top),get_amount()*left_edge,get_blend_method());
@@ -529,9 +559,9 @@ Rectangle::get_bounding_rect()const
        if(invert)
                return Rect::full_plane();
 
-       Point max(point1),min(point2);  
+       Point max(point1),min(point2);
        if((min[0] > max[0]))swap(min[0],max[0]);
-       if((min[1] > max[1]))swap(min[1],max[1]);       
+       if((min[1] > max[1]))swap(min[1],max[1]);
        if(min[0] > max[0])
        {
                min[0]+=expand;
@@ -566,9 +596,9 @@ Rectangle::get_full_bounding_rect(Context context)const
        {
                if(is_solid_color() && color.get_a()==0)
                {
-                       Point max(point1),min(point2);  
+                       Point max(point1),min(point2);
                        if((min[0] > max[0]))swap(min[0],max[0]);
-                       if((min[1] > max[1]))swap(min[1],max[1]);       
+                       if((min[1] > max[1]))swap(min[1],max[1]);
                        if(min[0] > max[0])
                        {
                                min[0]+=expand;
@@ -595,7 +625,7 @@ Rectangle::get_full_bounding_rect(Context context)const
 
                        return bounds & context.get_full_bounding_rect();
                }
-               return Rect::full_plane();                      
+               return Rect::full_plane();
        }
 
        return Layer_Composite::get_full_bounding_rect(context);