X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Ftrunk%2Fsrc%2Fsynfig%2Fcanvas.cpp;h=c50b901e73e2656ecb3d6b2a878bd9adda34194e;hb=756c0d29ac1742f231e6615f9a577e574e35a4af;hp=df200bafa3bb3b2aca5a858ca939e8c625a2245e;hpb=28f28705612902c15cd0702cc891fba35bf2d2df;p=synfig.git diff --git a/synfig-core/trunk/src/synfig/canvas.cpp b/synfig-core/trunk/src/synfig/canvas.cpp index df200ba..c50b901 100644 --- a/synfig-core/trunk/src/synfig/canvas.cpp +++ b/synfig-core/trunk/src/synfig/canvas.cpp @@ -2,19 +2,21 @@ /*! \file canvas.cpp ** \brief Canvas Class Member Definitions ** -** $Id: canvas.cpp,v 1.1.1.1 2005/01/04 01:23:14 darco Exp $ +** $Id$ ** ** \legal -** Copyright (c) 2002 Robert B. Quattlebaum Jr. +** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley +** 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 */ /* ========================================================================= */ @@ -67,8 +69,9 @@ int _CanvasCounter::counter(0); /* === M E T H O D S ======================================================= */ -Canvas::Canvas(const string &id): +Canvas::Canvas(const String &id): id_ (id), + version_ (CURRENT_CANVAS_VERSION), cur_time_ (0), is_inline_ (false), is_dirty_ (true), @@ -87,9 +90,28 @@ Canvas::on_changed() Canvas::~Canvas() { + // we were having a crash where pastecanvas layers were still + // refering to a canvas after it had been destroyed; this code + // will stop the pastecanvas layers from refering to the canvas + // before the canvas is destroyed + + // the set_sub_canvas(0) ends up deleting the parent-child link, + // which deletes the current element from the set we're iterating + // through, so we have to make sure we've incremented the iterator + // before we mess with the pastecanvas + std::set::iterator iter = parent_set.begin(); + while (iter != parent_set.end()) + { + Layer_PasteCanvas* paste_canvas = dynamic_cast(*iter); + iter++; + if(paste_canvas) + paste_canvas->set_sub_canvas(0); + else + warning("destroyed canvas has a parent that is not a pastecanvas - please report if repeatable"); + } + //if(is_inline() && parent_) assert(0); _CanvasCounter::counter--; - //DEBUGPOINT(); clear(); begin_delete(); } @@ -131,7 +153,7 @@ Canvas::clear() { Layer::Handle layer(front()); //if(layer->count()>2)synfig::info("before layer->count()=%d",layer->count()); - + erase(begin()); //if(layer->count()>1)synfig::info("after layer->count()=%d",layer->count()); } @@ -143,7 +165,7 @@ Canvas::clear() // would just continue going when polled // for a color. CanvasBase::push_back(Layer::Handle()); - + changed(); } @@ -206,10 +228,10 @@ valid_id(const String &x) { static const char bad_chars[]=" :#@$^&()*"; unsigned int i; - + if(!x.empty() && x[0]>='0' && x[0]<='9') return false; - + for(i=0;i(*this).cur_time_=t; - + is_dirty_=false; get_context().set_time(t); } @@ -313,34 +335,34 @@ Canvas::_get_relative_id(etl::loose_handle x)const if(x.get()==this) return String(); - + if(parent()==x.get()) return get_id(); - + String id; - + const Canvas* canvas=this; - + for(;!canvas->is_root();canvas=canvas->parent().get()) id=':'+canvas->get_id()+id; - + if(x && get_root()!=x->get_root()) { //String file_name=get_file_name(); //String file_path=x->get_file_path(); - + String file_name; if(is_absolute_path(get_file_name())) file_name=etl::relative_path(x->get_file_path(),get_file_name()); else file_name=get_file_name(); - + // If the path of X is inside of file_name, // then remove it. //if(file_name.size()>file_path.size()) // if(file_path==String(file_name,0,file_path.size())) // file_name.erase(0,file_path.size()+1); - + id=file_name+'#'+id; } @@ -362,7 +384,7 @@ Canvas::find_value_node(const String &id)const { if(is_inline() && parent_) return parent_->find_value_node(id); - + if(id.empty()) throw Exception::IDNotFound("Empty ID"); @@ -399,7 +421,7 @@ Canvas::surefind_value_node(const String &id) String value_node_id(id,id.rfind(':')+1); if(canvas_id.empty()) canvas_id=':'; - + return surefind_canvas(canvas_id)->value_node_list_.surefind(value_node_id); } @@ -410,7 +432,6 @@ Canvas::add_value_node(ValueNode::Handle x, const String &id) return parent_->add_value_node(x,id); // throw runtime_error("You cannot add a ValueNode to an inline Canvas"); - //DEBUGPOINT(); if(x->is_exported()) throw runtime_error("ValueNode is already exported"); @@ -419,30 +440,26 @@ Canvas::add_value_node(ValueNode::Handle x, const String &id) if(id.find_first_of(':',0)!=string::npos) throw Exception::BadLinkName("Bad character"); - + try { - //DEBUGPOINT(); if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id))) throw Exception::IDNotFound("add_value_node()"); - - //DEBUGPOINT(); + throw Exception::IDAlreadyExists(id); } catch(Exception::IDNotFound) { - //DEBUGPOINT(); x->set_id(id); - + x->set_parent_canvas(this); - + if(!value_node_list_.add(x)) { synfig::error("Unable to add ValueNode"); throw std::runtime_error("Unable to add ValueNode"); } - //DEBUGPOINT(); - + return; } } @@ -465,7 +482,7 @@ Canvas::rename_value_node(ValueNode::Handle x, const String &id) } catch(Exception::IDNotFound) { - x->set_id(id); + x->set_id(id); return; } @@ -478,15 +495,15 @@ Canvas::remove_value_node(ValueNode::Handle x) if(is_inline() && parent_) return parent_->remove_value_node(x); // throw Exception::IDNotFound("Canvas::remove_value_node() was called from an inline canvas"); - + if(!x) throw Exception::IDNotFound("Canvas::remove_value_node() was passed empty handle"); - + if(!value_node_list_.erase(x)) throw Exception::IDNotFound("Canvas::remove_value_node(): ValueNode was not found inside of this canvas"); //x->set_parent_canvas(0); - + x->set_id(""); } @@ -496,25 +513,27 @@ Canvas::surefind_canvas(const String &id) { if(is_inline() && parent_) return parent_->surefind_canvas(id); - + if(id.empty()) return this; - + // If the ID contains a "#" character, then a filename is - // expected on the left side. + // expected on the left side. if(id.find_first_of('#')!=string::npos) { // If '#' is the first character, remove it // and attempt to parse the ID again. if(id[0]=='#') return surefind_canvas(String(id,1)); - - //! \todo This needs alot more optimization + + //! \todo This needs a lot more optimization String file_name(id,0,id.find_first_of('#')); String external_id(id,id.find_first_of('#')+1); - + + file_name=unix_to_local_path(file_name); + Canvas::Handle external_canvas; - + // If the composition is already open, then use it. if(externals_.count(file_name)) external_canvas=externals_[file_name]; @@ -523,44 +542,44 @@ Canvas::surefind_canvas(const String &id) if(is_absolute_path(file_name)) external_canvas=open_canvas(file_name); else - external_canvas=open_canvas(get_file_path()+'/'+file_name); + external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name); if(!external_canvas) throw Exception::FileNotFound(file_name); externals_[file_name]=external_canvas; } - + return Handle::cast_const(external_canvas.constant()->find_canvas(external_id)); } - + // If we do not have any resolution, then we assume that the // request is for this immediate canvas if(id.find_first_of(':')==string::npos) { Children::iterator iter; - + // Search for the image in the image list, // and return it if it is found for(iter=children().begin();iter!=children().end();iter++) if(id==(*iter)->get_id()) return *iter; - + // Create a new canvas and return it //synfig::warning("Implicitly creating canvas named "+id); return new_child_canvas(id); } - - // If the first character is the seperator, then + + // If the first character is the separator, then // this references the root canvas. if(id[0]==':') return get_root()->surefind_canvas(string(id,1)); // Now we know that the requested Canvas is in a child - // of this canvas. We have to find that canvas and + // of this canvas. We have to find that canvas and // call "find_canvas" on it, and return the result. - + String canvas_name=string(id,0,id.find_first_of(':')); - + Canvas::Handle child_canvas=surefind_canvas(canvas_name); return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1)); @@ -584,20 +603,22 @@ Canvas::find_canvas(const String &id)const return this; // If the ID contains a "#" character, then a filename is - // expected on the left side. + // expected on the left side. if(id.find_first_of('#')!=string::npos) { // If '#' is the first character, remove it // and attempt to parse the ID again. if(id[0]=='#') return find_canvas(String(id,1)); - - //! \todo This needs alot more optimization + + //! \todo This needs a lot more optimization String file_name(id,0,id.find_first_of('#')); String external_id(id,id.find_first_of('#')+1); - + + file_name=unix_to_local_path(file_name); + Canvas::Handle external_canvas; - + // If the composition is already open, then use it. if(externals_.count(file_name)) external_canvas=externals_[file_name]; @@ -606,42 +627,42 @@ Canvas::find_canvas(const String &id)const if(is_absolute_path(file_name)) external_canvas=open_canvas(file_name); else - external_canvas=open_canvas(get_file_path()+'/'+file_name); + external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name); if(!external_canvas) throw Exception::FileNotFound(file_name); externals_[file_name]=external_canvas; } - + return Handle::cast_const(external_canvas.constant()->find_canvas(external_id)); } - + // If we do not have any resolution, then we assume that the // request is for this immediate canvas if(id.find_first_of(':')==string::npos) { Children::const_iterator iter; - + // Search for the image in the image list, // and return it if it is found for(iter=children().begin();iter!=children().end();iter++) if(id==(*iter)->get_id()) return *iter; - + throw Exception::IDNotFound("Child Canvas in Parent Canvas: (child)"+id); } - - // If the first character is the seperator, then + + // If the first character is the separator, then // this references the root canvas. if(id.find_first_of(':')==0) return get_root()->find_canvas(string(id,1)); // Now we know that the requested Canvas is in a child - // of this canvas. We have to find that canvas and + // of this canvas. We have to find that canvas and // call "find_canvas" on it, and return the result. - + String canvas_name=string(id,0,id.find_first_of(':')); - + Canvas::ConstHandle child_canvas=find_canvas(canvas_name); return child_canvas->find_canvas(string(id,id.find_first_of(':')+1)); @@ -657,7 +678,6 @@ Canvas::create() void Canvas::push_back(etl::handle x) { -// DEBUGPOINT(); // int i(x->count()); insert(end(),x); //if(x->count()!=i+1)synfig::info("push_back before %d, after %d",i,x->count()); @@ -666,7 +686,6 @@ Canvas::push_back(etl::handle x) void Canvas::push_front(etl::handle x) { -// DEBUGPOINT(); // int i(x->count()); insert(begin(),x); //if(x->count()!=i+1)synfig::error("push_front before %d, after %d",i,x->count()); @@ -690,29 +709,27 @@ Canvas::insert(iterator iter,etl::handle x) add_child(x.get()); - + LooseHandle correct_canvas(this); //while(correct_canvas->is_inline())correct_canvas=correct_canvas->parent(); Layer::LooseHandle loose_layer(x); - - x->signal_added_to_group().connect( - sigc::bind( - sigc::mem_fun( - *correct_canvas, - &Canvas::add_group_pair - ), - loose_layer - ) - ); - x->signal_removed_from_group().connect( - sigc::bind( - sigc::mem_fun( - *correct_canvas, - &Canvas::remove_group_pair - ), - loose_layer - ) - ); + + add_connection(loose_layer, + sigc::connection::connection( + x->signal_added_to_group().connect( + sigc::bind( + sigc::mem_fun( + *correct_canvas, + &Canvas::add_group_pair), + loose_layer)))); + add_connection(loose_layer, + sigc::connection::connection( + x->signal_removed_from_group().connect( + sigc::bind( + sigc::mem_fun( + *correct_canvas, + &Canvas::remove_group_pair), + loose_layer)))); if(!x->get_group().empty()) @@ -730,11 +747,11 @@ Canvas::push_back_simple(etl::handle x) } void -Canvas::erase(Canvas::iterator iter) +Canvas::erase(iterator iter) { if(!(*iter)->get_group().empty()) remove_group_pair((*iter)->get_group(),(*iter)); - + // HACK: We really shouldn't be wiping // out these signals entirely. We should // only be removing the specific connections @@ -744,11 +761,13 @@ Canvas::erase(Canvas::iterator iter) // is using these signals, so I'll just // leave these next two lines like they // are for now - darco 07-30-2004 - (*iter)->signal_added_to_group().clear(); - (*iter)->signal_removed_from_group().clear(); + + // so don't wipe them out entirely + // - dooglus 09-21-2007 + disconnect_connections(*iter); if(!op_flag_)remove_child(iter->get()); - + CanvasBase::erase(iter); if(!op_flag_)changed(); } @@ -762,16 +781,20 @@ Canvas::clone(const GUID& deriv_guid)const else { name=get_id()+"_CLONE"; - - throw runtime_error("Cloning of non-inline canvases is not yet suported"); + + throw runtime_error("Cloning of non-inline canvases is not yet supported"); } - + Handle canvas(new Canvas(name)); - + if(is_inline()) { canvas->is_inline_=true; - canvas->parent_=0; + // \todo this was setting parent_=0 - is there a reason for that? + // this was causing bug 1838132, where cloning an inline canvas that contains an imported image fails + // it was failing to ascertain the absolute pathname of the imported image, since it needs the pathname + // of the canvas to get that, which is stored in the parent canvas + canvas->parent_=parent(); //canvas->set_inline(parent()); } @@ -813,9 +836,9 @@ Canvas::set_inline(LooseHandle parent) { if(is_inline_ && parent_) { - + } - + id_="inline"; is_inline_=true; parent_=parent; @@ -828,7 +851,7 @@ Canvas::set_inline(LooseHandle parent) { parent->group_db_[iter->first].insert(iter->second.begin(),iter->second.end()); } - + rend_desc()=parent->rend_desc(); } @@ -838,7 +861,7 @@ Canvas::create_inline(Handle parent) assert(parent); //if(parent->is_inline()) // return create_inline(parent->parent()); - + Handle canvas(new Canvas("inline")); canvas->set_inline(parent); return canvas; @@ -872,7 +895,7 @@ Canvas::new_child_canvas(const String &id) // Create a new canvas children().push_back(create()); Canvas::Handle canvas(children().back()); - + canvas->set_id(id); canvas->parent_=this; canvas->rend_desc()=rend_desc(); @@ -888,10 +911,10 @@ Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id) if(child_canvas->parent() && !child_canvas->is_inline()) throw std::runtime_error("Cannot add child canvas because it belongs to someone else!"); - + if(!valid_id(id)) throw runtime_error("Invalid ID"); - + try { find_canvas(id); @@ -905,7 +928,7 @@ Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id) children().push_back(child_canvas); child_canvas->parent_=this; } - + return child_canvas; } @@ -914,13 +937,13 @@ Canvas::remove_child_canvas(Canvas::Handle child_canvas) { if(is_inline() && parent_) return parent_->remove_child_canvas(child_canvas); - + if(child_canvas->parent_!=this) throw runtime_error("Given child does not belong to me"); - + if(find(children().begin(),children().end(),child_canvas)==children().end()) throw Exception::IDNotFound(child_canvas->get_id()); - + children().remove(child_canvas); child_canvas->parent_=0; @@ -963,7 +986,7 @@ Canvas::get_file_path()const return dirname(file_name_); } - + String Canvas::get_meta_data(const String& key)const { @@ -1003,24 +1026,73 @@ Canvas::get_meta_data_keys()const for(iter=meta_data_.begin();!(iter==meta_data_.end());++iter) ret.push_back(iter->first); - + return ret; } +/* note - the "Motion Blur" and "Duplicate" layers need the dynamic + parameters of any PasteCanvas layers they loop over to be + maintained. When the variables in the following function + refer to "motion blur", they mean either of these two + layers. */ void -synfig::optimize_layers(Context context, Canvas::Handle op_canvas) +synfig::optimize_layers(Time time, Context context, Canvas::Handle op_canvas, bool seen_motion_blur_in_parent) { Context iter; std::vector< std::pair > sort_list; - int i; - + int i, motion_blur_i=0; // motion_blur_i is for resolving which layer comes first in the event of a z_depth tie + float motion_blur_z_depth=0; // the z_depth of the least deep motion blur layer in this context + bool seen_motion_blur_locally = false; + bool motion_blurred; // the final result - is this layer blurred or not? + + // If the parent didn't cause us to already be motion blurred, + // check whether there's a motion blur in this context, + // and if so, calculate its z_depth. + if (!seen_motion_blur_in_parent) + for(iter=context,i=0;*iter;iter++,i++) + { + Layer::Handle layer=*iter; + + // If the layer isn't active, don't worry about it + if(!layer->active()) + continue; + + // Any layer with an amount of zero is implicitly disabled. + ValueBase value(layer->get_param("amount")); + if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0) + continue; + + if(layer->get_name()=="MotionBlur" || layer->get_name()=="duplicate") + { + float z_depth(layer->get_z_depth()*1.0001+i); + + // If we've seen a motion blur before in this context... + if (seen_motion_blur_locally) + { + // ... then we're only interested in this one if it's less deep... + if (z_depth < motion_blur_z_depth) + { + motion_blur_z_depth = z_depth; + motion_blur_i = i; + } + } + // ... otherwise we're always interested in it. + else + { + motion_blur_z_depth = z_depth; + motion_blur_i = i; + seen_motion_blur_locally = true; + } + } + } + // Go ahead and start romping through the canvas to paste for(iter=context,i=0;*iter;iter++,i++) { Layer::Handle layer=*iter; float z_depth(layer->get_z_depth()*1.0001+i); - + // If the layer isn't active, don't worry about it if(!layer->active()) continue; @@ -1030,59 +1102,138 @@ synfig::optimize_layers(Context context, Canvas::Handle op_canvas) if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0) continue; - Layer_PasteCanvas* paste_canvas(static_cast(layer.get())); - if(layer->get_name()=="PasteCanvas" && paste_canvas->get_time_offset()==0) + // note: this used to include "&& paste_canvas->get_time_offset()==0", but then + // time-shifted layers weren't being sorted by z-depth (bug #1806852) + if(layer->get_name()=="PasteCanvas") { + Layer_PasteCanvas* paste_canvas(static_cast(layer.get())); + + // we need to blur the sub canvas if: + // our parent is blurred, + // or the child is lower than a local blur, + // or the child is at the same z_depth as a local blur, but later in the context + +#if 0 // DEBUG + if (seen_motion_blur_in_parent) synfig::info("seen BLUR in parent\n"); + else if (seen_motion_blur_locally) + if (z_depth > motion_blur_z_depth) synfig::info("paste is deeper than BLUR\n"); + else if (z_depth == motion_blur_z_depth) { synfig::info("paste is same depth as BLUR\n"); + if (i > motion_blur_i) synfig::info("paste is physically deeper than BLUR\n"); + else synfig::info("paste is less physically deep than BLUR\n"); + } else synfig::info("paste is less deep than BLUR\n"); + else synfig::info("no BLUR at all\n"); +#endif // DEBUG + + motion_blurred = (seen_motion_blur_in_parent || + (seen_motion_blur_locally && + (z_depth > motion_blur_z_depth || + (z_depth == motion_blur_z_depth && i > motion_blur_i)))); + Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas)); - optimize_layers(paste_canvas->get_sub_canvas()->get_context(),sub_canvas); -//#define SYNFIG_OPTIMIZE_PASTE_CANVAS 1 + Canvas::Handle paste_sub_canvas = paste_canvas->get_sub_canvas(); + if(paste_sub_canvas) + optimize_layers(time, paste_sub_canvas->get_context(),sub_canvas,motion_blurred); -#ifdef SYNFIG_OPTIMIZE_PASTE_CANVAS +// \todo: uncommenting the following breaks the rendering of at least examples/backdrop.sifz quite severely +// #define SYNFIG_OPTIMIZE_PASTE_CANVAS +#ifdef SYNFIG_OPTIMIZE_PASTE_CANVAS Canvas::iterator sub_iter; - // Determine if we can just remove the paste canvas - // altogether - if(paste_canvas->get_blend_method()==Color::BLEND_COMPOSITE && paste_canvas->get_amount()==1.0f && paste_canvas->get_zoom()==0 && paste_canvas->get_time_offset()==0 && paste_canvas->get_origin()==Point(0,0)) - try{ - for(sub_iter=sub_canvas->begin();sub_iter!=sub_canvas->end();++sub_iter) - { - Layer* layer=sub_iter->get(); - - // any layers that deform end up breaking things - // so do things the old way if we run into anything like this - if(!dynamic_cast(layer)) - throw int(); - - ValueBase value(layer->get_param("blend_method")); - if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE) - throw int(); - } - - // It has turned out that we don't need a paste canvas - // layer, so just go ahead and add all the layers onto - // the current stack and be done with it - while(sub_canvas->size()) - { - sort_list.push_back(std::pair(z_depth,sub_canvas->front())); - //op_canvas->push_back_simple(sub_canvas->front()); - sub_canvas->pop_front(); + + // Determine if we can just remove the paste canvas altogether + if (paste_canvas->get_blend_method() == Color::BLEND_COMPOSITE && + paste_canvas->get_amount() == 1.0f && + paste_canvas->get_zoom() == 0 && + paste_canvas->get_time_offset() == 0 && + paste_canvas->get_origin() == Point(0,0) ) + try { + for(sub_iter=sub_canvas->begin();sub_iter!=sub_canvas->end();++sub_iter) + { + Layer* layer=sub_iter->get(); + + // any layers that deform end up breaking things + // so do things the old way if we run into anything like this + if(!dynamic_cast(layer)) + throw int(); + + ValueBase value(layer->get_param("blend_method")); + if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE) + throw int(); + } + + // It has turned out that we don't need a paste canvas + // layer, so just go ahead and add all the layers onto + // the current stack and be done with it + while(sub_canvas->size()) + { + sort_list.push_back(std::pair(z_depth,sub_canvas->front())); + //op_canvas->push_back_simple(sub_canvas->front()); + sub_canvas->pop_front(); + } + continue; } - continue; - }catch(int) { } -#endif + catch(int) + { } +#endif // SYNFIG_OPTIMIZE_PASTE_CANVAS + Layer::Handle new_layer(Layer::create("PasteCanvas")); - dynamic_cast(new_layer.get())->set_do_not_muck_with_time(true); + dynamic_cast(new_layer.get())->set_muck_with_time(false); + if (motion_blurred) + { + Layer::DynamicParamList dynamic_param_list(paste_canvas->dynamic_param_list()); + for(Layer::DynamicParamList::const_iterator iter(dynamic_param_list.begin()); iter != dynamic_param_list.end(); ++iter) + new_layer->connect_dynamic_param(iter->first, iter->second); + } Layer::ParamList param_list(paste_canvas->get_param_list()); //param_list.erase("canvas"); new_layer->set_param_list(param_list); dynamic_cast(new_layer.get())->set_sub_canvas(sub_canvas); - dynamic_cast(new_layer.get())->set_do_not_muck_with_time(false); + dynamic_cast(new_layer.get())->set_muck_with_time(true); layer=new_layer; } - + else // not a PasteCanvas - does it use blend method 'Straight'? + { + /* when we use the 'straight' blend method, every pixel on the layer affects the layers underneath, + * not just the non-transparent pixels; the following workarea wraps non-pastecanvas layers in a + * new pastecanvas to ensure that the straight blend affects the full plane, not just the area + * within the layer's bounding box + */ + + // \todo: this code probably needs modification to work properly with motionblur and duplicate + etl::handle composite = etl::handle::cast_dynamic(layer); + + /* some layers (such as circle) don't touch pixels that aren't + * part of the circle, so they don't get blended correctly when + * using a straight blend. so we encapsulate the circle, and the + * encapsulation layer takes care of the transparent pixels + * for us. if we do that for all layers, however, then the + * distortion layers no longer work, since they have no + * context to work on. the Layer::reads_context() method + * returns true for layers which need to be able to see + * their context. we can't encapsulate those. + */ + if (composite && + Color::is_straight(composite->get_blend_method()) && + !composite->reads_context()) + { + Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas)); + sub_canvas->push_back(composite = composite->clone()); + layer = Layer::create("PasteCanvas"); + composite->set_description(strprintf("Wrapped clone of '%s'", composite->get_non_empty_description().c_str())); + layer->set_description(strprintf("PasteCanvas wrapper for '%s'", composite->get_non_empty_description().c_str())); + Layer_PasteCanvas* paste_canvas(static_cast(layer.get())); + paste_canvas->set_blend_method(composite->get_blend_method()); + paste_canvas->set_amount(composite->get_amount()); + sub_canvas->set_time(time); // region and outline don't calculate their bounding rects until their time is set + composite->set_blend_method(Color::BLEND_STRAIGHT); // do this before calling set_sub_canvas(), but after set_time() + composite->set_amount(1.0f); // after set_time() + paste_canvas->set_sub_canvas(sub_canvas); + } + } + sort_list.push_back(std::pair(z_depth,layer)); - //op_canvas->push_back_simple(layer); + //op_canvas->push_back_simple(layer); } - + //sort_list.sort(); stable_sort(sort_list.begin(),sort_list.end()); std::vector< std::pair >::iterator iter2; @@ -1096,7 +1247,7 @@ Canvas::get_times_vfunc(Node::time_set &set) const { const_iterator i = begin(), iend = end(); - + for(; i != iend; ++i) { const Node::time_set &tset = (*i)->get_times(); @@ -1136,7 +1287,7 @@ Canvas::get_group_count()const return group_db_.size(); } - + void Canvas::add_group_pair(String group, etl::handle layer) { @@ -1145,7 +1296,7 @@ Canvas::add_group_pair(String group, etl::handle layer) signal_group_added()(group); else signal_group_changed()(group); - + signal_group_pair_added()(group,layer); if(is_inline() && parent_) @@ -1172,11 +1323,26 @@ Canvas::remove_group_pair(String group, etl::handle layer) } void +Canvas::add_connection(etl::loose_handle layer, sigc::connection connection) +{ + connections_[layer].push_back(connection); +} + +void +Canvas::disconnect_connections(etl::loose_handle layer) +{ + std::vector::iterator iter; + for(iter=connections_[layer].begin();iter!=connections_[layer].end();++iter) + iter->disconnect(); + connections_[layer].clear(); +} + +void Canvas::rename_group(const String&old_name,const String&new_name) { if(is_inline() && parent_) return parent_->rename_group(old_name,new_name); - + { std::map > >::iterator iter; iter=group_db_.find(old_name); @@ -1188,10 +1354,10 @@ Canvas::rename_group(const String&old_name,const String&new_name) rename_group(iter->first,name); } } - + std::set > layers(get_layers_in_group(old_name)); std::set >::iterator iter; - + for(iter=layers.begin();iter!=layers.end();++iter) { (*iter)->remove_from_group(old_name);