Remove .gitignore do nothing is ignored.
[synfig.git] / synfig-core / trunk / src / synfig / context.cpp
index ae8a036..38e0f67 100644 (file)
@@ -1,11 +1,12 @@
 /* === S Y N F I G ========================================================= */
-/*!    \file template.cpp
+/*!    \file context.cpp
 **     \brief Template File
 **
-**     $Id: context.cpp,v 1.4 2005/01/24 05:00:18 darco Exp $
+**     $Id$
 **
 **     \legal
 **     Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
+**     Copyright (c) 2008 Chris Moore
 **
 **     This package is free software; you can redistribute it and/or
 **     modify it under the terms of the GNU General Public License as
@@ -31,6 +32,7 @@
 
 #include "context.h"
 #include "layer.h"
+#include "layer_composite.h"
 #include "string.h"
 #include "vector.h"
 #include "color.h"
@@ -48,8 +50,8 @@ using namespace synfig;
 
 /* === M A C R O S ========================================================= */
 
-//#define SYNFIG_PROFILE_LAYERS
-//#define SYNFIG_DEBUG_LAYERS
+// #define SYNFIG_PROFILE_LAYERS
+// #define SYNFIG_DEBUG_LAYERS
 
 /* === G L O B A L S ======================================================= */
 
@@ -77,8 +79,7 @@ _print_profile_report()
        synfig::info("Total Time: %f seconds", total_time);
        synfig::info("<<<< End of Profile Report");
 }
-
-#endif
+#endif // SYNFIG_PROFILE_LAYERS
 
 /* === P R O C E D U R E S ================================================= */
 
@@ -88,14 +89,14 @@ Color
 Context::get_color(const Point &pos)const
 {
        Context context(*this);
-               
+
        while(!context->empty())
-       {       
+       {
                // If this layer is active, then go
                // ahead and break out of the loop
                if((*context)->active())
                        break;
-               
+
                // Otherwise, we want to keep searching
                // till we find either an active layer,
                // or the end of the layer list
@@ -114,24 +115,24 @@ Rect
 Context::get_full_bounding_rect()const
 {
        Context context(*this);
-               
+
        while(!context->empty())
-       {       
+       {
                // If this layer is active, then go
                // ahead and break out of the loop
                if((*context)->active())
                        break;
-               
+
                // Otherwise, we want to keep searching
                // till we find either an active layer,
                // or the end of the layer list
                ++context;
        }
 
-       if(context->empty())
-               return Rect::zero();
+       // If this layer isn't defined, return zero-sized rectangle
+       if(context->empty()) return Rect::zero();
 
-       return (*context)->get_full_bounding_rect(*this+1);
+       return (*context)->get_full_bounding_rect(context+1);
 }
 
 
@@ -157,7 +158,7 @@ Context::get_full_bounding_rect()const
          -
          +
        -
-       
+
        at each minus we must record all the info for that which we are worried about...
        each layer can do work before or after the other work is done... so both values must be recorded...
 */
@@ -165,9 +166,9 @@ Context::get_full_bounding_rect()const
 bool
 Context::accelerated_render(Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb) const
 {
-       #ifdef SYNFIG_PROFILE_LAYERS
+#ifdef SYNFIG_PROFILE_LAYERS
        String layer_name(curr_layer);
-       
+
        //sum the pre-work done by layer above us... (curr_layer is layer above us...)
        if(depth>0)
        {
@@ -175,63 +176,112 @@ Context::accelerated_render(Surface *surface,int quality, const RendDesc &rendde
                //if(run_table.count(curr_layer))run_table[curr_layer]++;
                //      else run_table[curr_layer]=1;
        }
-       #endif
+#endif // SYNFIG_PROFILE_LAYERS
 
        const Rect bbox(renddesc.get_rect());
-       
+
+       // this is going to be set to true if this layer contributes
+       // nothing, but it's a straight blend with non-zero amount, and so
+       // it has an effect anyway
+       bool straight_and_empty = false;
+       etl::handle<Layer_Composite> composite;
        Context context(*this);
+
        for(;!(context)->empty();++context)
        {
-               // If we are not active
-               // then move on to next layer
+               // If we are not active then move on to next layer
                if(!(*context)->active())
                        continue;
-               
+
                const Rect layer_bounds((*context)->get_bounding_rect());
-               
-               // If the box area is less than zero
-               // then move on to next layer
-               if(layer_bounds.area()<=0.0000000000001)
-                       continue;
-               
-               // If the boxes do not intersect
-               // then move on to next layer
-               if(!(layer_bounds && bbox))
+               composite = etl::handle<Layer_Composite>::cast_dynamic(*context);
+
+               // If the box area is less than zero or the boxes do not
+               // intersect then move on to next layer, unless the layer is
+               // using a straight blend and has a non-zero amount, in which
+               // case it will still affect the result
+               if(layer_bounds.area() <= 0.0000000000001 || !(layer_bounds && bbox))
+               {
+                       if (composite &&
+                               Color::is_straight(composite->get_blend_method()) &&
+                               composite->get_amount() != 0.0f)
+                       {
+                               straight_and_empty = true;
+                               break;
+                       }
                        continue;
-               
+               }
+
+               // If this layer has Straight as the blend method and amount
+               // is 1.0, and the layer doesn't depend on its context, then
+               // we don't want to render the context
+               if (composite &&
+                       composite->get_blend_method() == Color::BLEND_STRAIGHT &&
+                       composite->get_amount() == 1.0f &&
+                       !composite->reads_context())
+               {
+                       Layer::Handle layer = *context;
+                       while (!context->empty()) context++; // skip the context
+                       return layer->accelerated_render(context,surface,quality,renddesc, cb);
+               }
+
                // Break out of the loop--we have found a good layer
                break;
        }
 
        // If this layer isn't defined, return alpha
-       if((context)->empty())
+       if (context->empty() || (straight_and_empty && composite->get_amount() == 1.0f))
        {
 #ifdef SYNFIG_DEBUG_LAYERS
                synfig::info("Context::accelerated_render(): Hit end of list");
-#endif
+#endif // SYNFIG_DEBUG_LAYERS
                surface->set_wh(renddesc.get_w(),renddesc.get_h());
                surface->clear();
-               #ifdef SYNFIG_PROFILE_LAYERS
-               profile_timer.reset();  
-               #endif
+#ifdef SYNFIG_PROFILE_LAYERS
+               profile_timer.reset();
+#endif // SYNFIG_PROFILE_LAYERS
                return true;
        }
 
 #ifdef SYNFIG_DEBUG_LAYERS
        synfig::info("Context::accelerated_render(): Descending into %s",(*context)->get_name().c_str());
-#endif
+#endif // SYNFIG_DEBUG_LAYERS
 
        try {
                RWLock::ReaderLock lock((*context)->get_rw_lock());
-               
-       #ifdef SYNFIG_PROFILE_LAYERS
-       
+
+#ifdef SYNFIG_PROFILE_LAYERS
        //go down one layer :P
        depth++;
        curr_layer=(*context)->get_name();      //make sure the layer inside is referring to the correct layer outside
        profile_timer.reset();                                                                          // +
-       bool ret((*context)->accelerated_render(context+1,surface,quality,renddesc, cb));
-       
+#endif // SYNFIG_PROFILE_LAYERS
+
+       bool ret;
+
+       // this layer doesn't draw anything onto the canvas we're
+       // rendering, but it uses straight blending, so we need to render
+       // the stuff under us and then blit transparent pixels over it
+       // using the appropriate 'amount'
+       if (straight_and_empty)
+       {
+               if ((ret = Context((context+1)).accelerated_render(surface,quality,renddesc,cb)))
+               {
+                       Surface clearsurface;
+                       clearsurface.set_wh(renddesc.get_w(),renddesc.get_h());
+                       clearsurface.clear();
+
+                       Surface::alpha_pen apen(surface->begin());
+                       apen.set_alpha(composite->get_amount());
+                       apen.set_blend_method(composite->get_blend_method());
+
+                       clearsurface.blit_to(apen);
+               }
+       }
+       else
+               ret = (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
+
+#ifdef SYNFIG_PROFILE_LAYERS
        //post work for the previous layer
        time_table[curr_layer]+=profile_timer();                                                        //-
        if(run_table.count(curr_layer))run_table[curr_layer]++;
@@ -239,30 +289,28 @@ Context::accelerated_render(Surface *surface,int quality, const RendDesc &rendde
 
        depth--;
        curr_layer = layer_name; //we are now onto this layer (make sure the post gets recorded correctly...
-               
+
        //print out the table it we're done...
        if(depth==0) _print_profile_report(),time_table.clear(),run_table.clear();
        profile_timer.reset();                                                                                          //+
-       return ret;
-       #else
-       return (*context)->accelerated_render(context+1,surface,quality,renddesc, cb);
-       #endif
+#endif // SYNFIG_PROFILE_LAYERS
 
+       return ret;
        }
        catch(std::bad_alloc)
        {
                synfig::error("Context::accelerated_render(): Layer \"%s\" threw a bad_alloc exception!",(*context)->get_name().c_str());
 #ifdef _DEBUG
                return false;
-#else
+#else  // _DEBUG
                ++context;
                return context.accelerated_render(surface, quality, renddesc, cb);
-#endif
+#endif // _DEBUG
        }
        catch(...)
        {
                synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str());
-               throw;          
+               throw;
        }
 }
 
@@ -271,12 +319,16 @@ Context::set_time(Time time)const
 {
        Context context(*this);
        while(!(context)->empty())
-       {       
-               // If this layer is active, then go
-               // ahead and break out of the loop
-               if((*context)->active() && !(*context)->dirty_time_.is_equal(time))
+       {
+               // If this layer is active, and
+               // it either isn't already set to the given time or
+               //           it's a time loop layer,
+               // then break out of the loop and set its time
+               if((*context)->active() &&
+                  (!(*context)->dirty_time_.is_equal(time) ||
+                       (*context)->get_name() == "timeloop"))
                        break;
-               
+
                // Otherwise, we want to keep searching
                // till we find either an active layer,
                // or the end of the layer list
@@ -286,7 +338,7 @@ Context::set_time(Time time)const
        // If this layer isn't defined, just return
        if((context)->empty()) return;
 
-       // Set up a wrter lock
+       // Set up a writer lock
        RWLock::WriterLock lock((*context)->get_rw_lock());
 
        //synfig::info("%s: dirty_time=%f",(*context)->get_name().c_str(),(float)(*context)->dirty_time_);
@@ -295,10 +347,10 @@ Context::set_time(Time time)const
        {
                Layer::ParamList params;
                Layer::DynamicParamList::const_iterator iter;
-               
+
                for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
                        params[iter->first]=(*iter->second)(time);
-               
+
                (*context)->set_param_list(params);
 
                (*context)->set_time(context+1,time);
@@ -308,18 +360,18 @@ Context::set_time(Time time)const
 }
 
 void
-Context::set_time(Time time,const Vector &pos)const
+Context::set_time(Time time,const Vector &/*pos*/)const
 {
        set_time(time);
 /*
        Context context(*this);
        while(!(context)->empty())
-       {       
+       {
                // If this layer is active, then go
                // ahead and break out of the loop
                if((*context)->active())
                        break;
-               
+
                // Otherwise, we want to keep searching
                // till we find either an active layer,
                // or the end of the layer list
@@ -333,10 +385,10 @@ Context::set_time(Time time,const Vector &pos)const
        {
                Layer::ParamList params;
                Layer::DynamicParamList::const_iterator iter;
-               
+
                for(iter=(*context)->dynamic_param_list().begin();iter!=(*context)->dynamic_param_list().end();iter++)
                        params[iter->first]=(*iter->second)(time);
-               
+
                (*context)->set_param_list(params);
 
                (*context)->set_time(context+1,time,pos);
@@ -348,14 +400,14 @@ etl::handle<Layer>
 Context::hit_check(const Point &pos)const
 {
        Context context(*this);
-       
+
        while(!context->empty())
-       {       
+       {
                // If this layer is active, then go
                // ahead and break out of the loop
                if((*context)->active())
                        break;
-               
+
                // Otherwise, we want to keep searching
                // till we find either an active layer,
                // or the end of the layer list