X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Fsrc%2Fsynfig%2Fcontext.cpp;fp=synfig-core%2Fsrc%2Fsynfig%2Fcontext.cpp;h=38e0f671684321f872798f77cc8c1f1d9872c545;hb=a095981e18cc37a8ecc7cd237cc22b9c10329264;hp=0000000000000000000000000000000000000000;hpb=9459638ad6797b8139f1e9f0715c96076dbf0890;p=synfig.git diff --git a/synfig-core/src/synfig/context.cpp b/synfig-core/src/synfig/context.cpp new file mode 100644 index 0000000..38e0f67 --- /dev/null +++ b/synfig-core/src/synfig/context.cpp @@ -0,0 +1,421 @@ +/* === S Y N F I G ========================================================= */ +/*! \file context.cpp +** \brief Template File +** +** $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 +** published by the Free Software Foundation; either version 2 of +** the License, or (at your option) any later version. +** +** 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 +*/ +/* ========================================================================= */ + +/* === H E A D E R S ======================================================= */ + +#ifdef USING_PCH +# include "pch.h" +#else +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "context.h" +#include "layer.h" +#include "layer_composite.h" +#include "string.h" +#include "vector.h" +#include "color.h" +#include "surface.h" +#include "renddesc.h" +#include "valuenode.h" + +#endif + +/* === U S I N G =========================================================== */ + +using namespace std; +using namespace etl; +using namespace synfig; + +/* === M A C R O S ========================================================= */ + +// #define SYNFIG_PROFILE_LAYERS +// #define SYNFIG_DEBUG_LAYERS + +/* === G L O B A L S ======================================================= */ + +#ifdef SYNFIG_PROFILE_LAYERS +#include +static int depth(0); +static std::map time_table; +static std::map run_table; +static etl::clock profile_timer; +static String curr_layer; +static void +_print_profile_report() +{ + synfig::info(">>>> Profile Report: (Times are in msecs)"); + std::map::iterator iter; + float total_time(0); + for(iter=time_table.begin();iter!=time_table.end();++iter) + { + String layer(iter->first); + float time(iter->second); + int runs(run_table[layer]); + total_time+=time; + synfig::info(" Layer \"%s\",\tExecs: %03d, Avg Time: %05.1f, Total Time: %05.1f",layer.c_str(),runs,time/runs*1000,time*1000); + } + synfig::info("Total Time: %f seconds", total_time); + synfig::info("<<<< End of Profile Report"); +} +#endif // SYNFIG_PROFILE_LAYERS + +/* === P R O C E D U R E S ================================================= */ + +/* === M E T H O D S ======================================================= */ + +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 + ++context; + } + + // If this layer isn't defined, return alpha + if((context)->empty()) return Color::alpha(); + + RWLock::ReaderLock lock((*context)->get_rw_lock()); + + return (*context)->get_color(context+1, pos); +} + +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 this layer isn't defined, return zero-sized rectangle + if(context->empty()) return Rect::zero(); + + return (*context)->get_full_bounding_rect(context+1); +} + + +/* Profiling will go like this: + Profile start = +, stop = - + + + + - + + time diff is recorded + + to get the independent times we need to break at the one inside and record etc... + so it looks more like this: + + + + - + + + - + + + ... + - + + + - + + + - + + 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... +*/ + +bool +Context::accelerated_render(Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb) const +{ +#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) + { + time_table[curr_layer]+=profile_timer(); + //if(run_table.count(curr_layer))run_table[curr_layer]++; + // else run_table[curr_layer]=1; + } +#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 composite; + Context context(*this); + + for(;!(context)->empty();++context) + { + // If we are not active then move on to next layer + if(!(*context)->active()) + continue; + + const Rect layer_bounds((*context)->get_bounding_rect()); + composite = etl::handle::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() || (straight_and_empty && composite->get_amount() == 1.0f)) + { +#ifdef SYNFIG_DEBUG_LAYERS + synfig::info("Context::accelerated_render(): Hit end of list"); +#endif // SYNFIG_DEBUG_LAYERS + surface->set_wh(renddesc.get_w(),renddesc.get_h()); + surface->clear(); +#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 // SYNFIG_DEBUG_LAYERS + + try { + RWLock::ReaderLock lock((*context)->get_rw_lock()); + +#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(); // + +#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]++; + else run_table[curr_layer]=1; + + 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(); //+ +#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 // _DEBUG + ++context; + return context.accelerated_render(surface, quality, renddesc, cb); +#endif // _DEBUG + } + catch(...) + { + synfig::error("Context::accelerated_render(): Layer \"%s\" threw an exception, rethrowing...",(*context)->get_name().c_str()); + throw; + } +} + +void +Context::set_time(Time time)const +{ + Context context(*this); + while(!(context)->empty()) + { + // 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 + ++context; + } + + // If this layer isn't defined, just return + if((context)->empty()) return; + + // 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_); + //synfig::info("%s: time=%f",(*context)->get_name().c_str(),(float)time); + + { + 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); + (*context)->dirty_time_=time; + + } +} + +void +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 + ++context; + } + + // If this layer isn't defined, just return + if((context)->empty()) return; + + else + { + 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); + } +*/ +} + +etl::handle +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 + ++context; + } + + // If this layer isn't defined, return an empty handle + if((context)->empty()) return 0; + + return (*context)->hit_check(context+1, pos); +}