1 /* === S Y N F I G ========================================================= */
3 ** \brief Canvas Class Member Definitions
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007 Chris Moore
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.
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.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
26 #define SYNFIG_NO_ANGLE
38 #include "exception.h"
41 #include "layer_pastecanvas.h"
42 #include <sigc++/bind.h>
46 using namespace synfig;
50 namespace synfig { extern Canvas::Handle open_canvas(const String &filename); };
52 /* === M A C R O S ========================================================= */
60 synfig::error("%d canvases not yet deleted!",counter);
64 int _CanvasCounter::counter(0);
66 /* === G L O B A L S ======================================================= */
68 /* === P R O C E D U R E S ================================================= */
70 /* === M E T H O D S ======================================================= */
72 Canvas::Canvas(const String &id):
74 version_ (CURRENT_CANVAS_VERSION),
80 _CanvasCounter::counter++;
93 //if(is_inline() && parent_) assert(0);
94 _CanvasCounter::counter--;
102 return CanvasBase::end()-1;
105 Canvas::const_iterator
108 return CanvasBase::end()-1;
111 Canvas::reverse_iterator
114 return CanvasBase::rbegin()+1;
117 Canvas::const_reverse_iterator
118 Canvas::rbegin()const
120 return CanvasBase::rbegin()+1;
126 return CanvasBase::size()-1;
134 Layer::Handle layer(front());
135 //if(layer->count()>2)synfig::info("before layer->count()=%d",layer->count());
138 //if(layer->count()>1)synfig::info("after layer->count()=%d",layer->count());
140 //CanvasBase::clear();
142 // We need to keep a blank handle at the
143 // end of the image list, and acts at
144 // the bottom. Without it, the layers
145 // would just continue going when polled
147 CanvasBase::push_back(Layer::Handle());
155 return CanvasBase::size()<=1;
161 return *(CanvasBase::end()-1);
164 const Layer::Handle &
167 return *(CanvasBase::end()-1);
171 Canvas::get_context()const
176 const ValueNodeList &
177 Canvas::value_node_list()const
179 if(is_inline() && parent_)
180 return parent_->value_node_list();
181 return value_node_list_;
185 Canvas::keyframe_list()
187 if(is_inline() && parent_)
188 return parent_->keyframe_list();
189 return keyframe_list_;
193 Canvas::keyframe_list()const
195 if(is_inline() && parent_)
196 return parent_->keyframe_list();
197 return keyframe_list_;
201 Canvas::find_layer(const Point &pos)
203 return get_context().hit_check(pos);
207 valid_id(const String &x)
209 static const char bad_chars[]=" :#@$^&()*";
212 if(!x.empty() && x[0]>='0' && x[0]<='9')
215 for(i=0;i<sizeof(bad_chars);i++)
216 if(x.find_first_of(bad_chars[i])!=string::npos)
223 Canvas::set_id(const String &x)
225 if(is_inline() && parent_)
226 throw runtime_error("Inline Canvas cannot have an ID");
229 throw runtime_error("Invalid ID");
231 signal_id_changed_();
235 Canvas::set_name(const String &x)
238 signal_meta_data_changed()("name");
239 signal_meta_data_changed("name")();
243 Canvas::set_author(const String &x)
246 signal_meta_data_changed()("author");
247 signal_meta_data_changed("author")();
251 Canvas::set_description(const String &x)
254 signal_meta_data_changed()("description");
255 signal_meta_data_changed("description")();
259 Canvas::set_time(Time t)const
261 if(is_dirty_ || !get_time().is_equal(t))
266 synfig::info("is_dirty_=%d",is_dirty_);
267 synfig::info("get_time()=%f",(float)get_time());
268 synfig::info("t=%f",(float)t);
273 const_cast<Canvas&>(*this).cur_time_=t;
276 get_context().set_time(t);
282 Canvas::get_root()const
284 return parent_?parent_->get_root().get():const_cast<synfig::Canvas *>(this);
288 Canvas::get_depth(etl::handle<Layer> layer)const
292 for(iter=begin();iter!=end();++iter,i++)
301 Canvas::get_relative_id(etl::loose_handle<const Canvas> x)const
303 if(x->get_root()==this)
305 if(is_inline() && parent_)
306 return parent_->_get_relative_id(x);
307 return _get_relative_id(x);
311 Canvas::_get_relative_id(etl::loose_handle<const Canvas> x)const
313 if(is_inline() && parent_)
314 return parent_->_get_relative_id(x);
319 if(parent()==x.get())
324 const Canvas* canvas=this;
326 for(;!canvas->is_root();canvas=canvas->parent().get())
327 id=':'+canvas->get_id()+id;
329 if(x && get_root()!=x->get_root())
331 //String file_name=get_file_name();
332 //String file_path=x->get_file_path();
335 if(is_absolute_path(get_file_name()))
336 file_name=etl::relative_path(x->get_file_path(),get_file_name());
338 file_name=get_file_name();
340 // If the path of X is inside of file_name,
342 //if(file_name.size()>file_path.size())
343 // if(file_path==String(file_name,0,file_path.size()))
344 // file_name.erase(0,file_path.size()+1);
354 Canvas::find_value_node(const String &id)
357 ValueNode::Handle::cast_const(
358 const_cast<const Canvas*>(this)->find_value_node(id)
362 ValueNode::ConstHandle
363 Canvas::find_value_node(const String &id)const
365 if(is_inline() && parent_)
366 return parent_->find_value_node(id);
369 throw Exception::IDNotFound("Empty ID");
371 // If we do not have any resolution, then we assume that the
372 // request is for this immediate canvas
373 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
374 return value_node_list_.find(id);
376 String canvas_id(id,0,id.rfind(':'));
377 String value_node_id(id,id.rfind(':')+1);
378 if(canvas_id.empty())
380 //synfig::warning("constfind:value_node_id: "+value_node_id);
381 //synfig::warning("constfind:canvas_id: "+canvas_id);
383 return find_canvas(canvas_id)->value_node_list_.find(value_node_id);
387 Canvas::surefind_value_node(const String &id)
389 if(is_inline() && parent_)
390 return parent_->surefind_value_node(id);
393 throw Exception::IDNotFound("Empty ID");
395 // If we do not have any resolution, then we assume that the
396 // request is for this immediate canvas
397 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
398 return value_node_list_.surefind(id);
400 String canvas_id(id,0,id.rfind(':'));
401 String value_node_id(id,id.rfind(':')+1);
402 if(canvas_id.empty())
405 return surefind_canvas(canvas_id)->value_node_list_.surefind(value_node_id);
409 Canvas::add_value_node(ValueNode::Handle x, const String &id)
411 if(is_inline() && parent_)
412 return parent_->add_value_node(x,id);
413 // throw runtime_error("You cannot add a ValueNode to an inline Canvas");
416 throw runtime_error("ValueNode is already exported");
419 throw Exception::BadLinkName("Empty ID");
421 if(id.find_first_of(':',0)!=string::npos)
422 throw Exception::BadLinkName("Bad character");
426 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
427 throw Exception::IDNotFound("add_value_node()");
429 throw Exception::IDAlreadyExists(id);
431 catch(Exception::IDNotFound)
435 x->set_parent_canvas(this);
437 if(!value_node_list_.add(x))
439 synfig::error("Unable to add ValueNode");
440 throw std::runtime_error("Unable to add ValueNode");
449 Canvas::rename_value_node(ValueNode::Handle x, const String &id)
452 throw Exception::BadLinkName("Empty ID");
454 if(id.find_first_of(": ",0)!=string::npos)
455 throw Exception::BadLinkName("Bad character");
459 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
460 throw Exception::IDNotFound("rename_value_node");
461 throw Exception::IDAlreadyExists(id);
463 catch(Exception::IDNotFound)
473 Canvas::remove_value_node(ValueNode::Handle x)
475 if(is_inline() && parent_)
476 return parent_->remove_value_node(x);
477 // throw Exception::IDNotFound("Canvas::remove_value_node() was called from an inline canvas");
480 throw Exception::IDNotFound("Canvas::remove_value_node() was passed empty handle");
482 if(!value_node_list_.erase(x))
483 throw Exception::IDNotFound("Canvas::remove_value_node(): ValueNode was not found inside of this canvas");
485 //x->set_parent_canvas(0);
492 Canvas::surefind_canvas(const String &id)
494 if(is_inline() && parent_)
495 return parent_->surefind_canvas(id);
500 // If the ID contains a "#" character, then a filename is
501 // expected on the left side.
502 if(id.find_first_of('#')!=string::npos)
504 // If '#' is the first character, remove it
505 // and attempt to parse the ID again.
507 return surefind_canvas(String(id,1));
509 //! \todo This needs a lot more optimization
510 String file_name(id,0,id.find_first_of('#'));
511 String external_id(id,id.find_first_of('#')+1);
513 file_name=unix_to_local_path(file_name);
515 Canvas::Handle external_canvas;
517 // If the composition is already open, then use it.
518 if(externals_.count(file_name))
519 external_canvas=externals_[file_name];
522 if(is_absolute_path(file_name))
523 external_canvas=open_canvas(file_name);
525 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name);
528 throw Exception::FileNotFound(file_name);
529 externals_[file_name]=external_canvas;
532 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id));
535 // If we do not have any resolution, then we assume that the
536 // request is for this immediate canvas
537 if(id.find_first_of(':')==string::npos)
539 Children::iterator iter;
541 // Search for the image in the image list,
542 // and return it if it is found
543 for(iter=children().begin();iter!=children().end();iter++)
544 if(id==(*iter)->get_id())
547 // Create a new canvas and return it
548 //synfig::warning("Implicitly creating canvas named "+id);
549 return new_child_canvas(id);
552 // If the first character is the separator, then
553 // this references the root canvas.
555 return get_root()->surefind_canvas(string(id,1));
557 // Now we know that the requested Canvas is in a child
558 // of this canvas. We have to find that canvas and
559 // call "find_canvas" on it, and return the result.
561 String canvas_name=string(id,0,id.find_first_of(':'));
563 Canvas::Handle child_canvas=surefind_canvas(canvas_name);
565 return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1));
569 Canvas::find_canvas(const String &id)
572 Canvas::Handle::cast_const(
573 const_cast<const Canvas*>(this)->find_canvas(id)
578 Canvas::find_canvas(const String &id)const
580 if(is_inline() && parent_)return parent_->find_canvas(id);
585 // If the ID contains a "#" character, then a filename is
586 // expected on the left side.
587 if(id.find_first_of('#')!=string::npos)
589 // If '#' is the first character, remove it
590 // and attempt to parse the ID again.
592 return find_canvas(String(id,1));
594 //! \todo This needs a lot more optimization
595 String file_name(id,0,id.find_first_of('#'));
596 String external_id(id,id.find_first_of('#')+1);
598 file_name=unix_to_local_path(file_name);
600 Canvas::Handle external_canvas;
602 // If the composition is already open, then use it.
603 if(externals_.count(file_name))
604 external_canvas=externals_[file_name];
607 if(is_absolute_path(file_name))
608 external_canvas=open_canvas(file_name);
610 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name);
613 throw Exception::FileNotFound(file_name);
614 externals_[file_name]=external_canvas;
617 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id));
620 // If we do not have any resolution, then we assume that the
621 // request is for this immediate canvas
622 if(id.find_first_of(':')==string::npos)
624 Children::const_iterator iter;
626 // Search for the image in the image list,
627 // and return it if it is found
628 for(iter=children().begin();iter!=children().end();iter++)
629 if(id==(*iter)->get_id())
632 throw Exception::IDNotFound("Child Canvas in Parent Canvas: (child)"+id);
635 // If the first character is the separator, then
636 // this references the root canvas.
637 if(id.find_first_of(':')==0)
638 return get_root()->find_canvas(string(id,1));
640 // Now we know that the requested Canvas is in a child
641 // of this canvas. We have to find that canvas and
642 // call "find_canvas" on it, and return the result.
644 String canvas_name=string(id,0,id.find_first_of(':'));
646 Canvas::ConstHandle child_canvas=find_canvas(canvas_name);
648 return child_canvas->find_canvas(string(id,id.find_first_of(':')+1));
655 return new Canvas("Untitled");
659 Canvas::push_back(etl::handle<Layer> x)
661 // int i(x->count());
663 //if(x->count()!=i+1)synfig::info("push_back before %d, after %d",i,x->count());
667 Canvas::push_front(etl::handle<Layer> x)
669 // int i(x->count());
671 //if(x->count()!=i+1)synfig::error("push_front before %d, after %d",i,x->count());
675 Canvas::insert(iterator iter,etl::handle<Layer> x)
677 // int i(x->count());
678 CanvasBase::insert(iter,x);
680 /*if(x->count()!=i+1)
682 synfig::error(__FILE__":%d: Canvas::insert(): ***FAILURE*** before %d, after %d",__LINE__,i,x->count());
684 //throw runtime_error("Canvas Insertion Failed");
693 LooseHandle correct_canvas(this);
694 //while(correct_canvas->is_inline())correct_canvas=correct_canvas->parent();
695 Layer::LooseHandle loose_layer(x);
697 add_connection(loose_layer,
698 sigc::connection::connection(
699 x->signal_added_to_group().connect(
703 &Canvas::add_group_pair),
705 add_connection(loose_layer,
706 sigc::connection::connection(
707 x->signal_removed_from_group().connect(
711 &Canvas::remove_group_pair),
715 if(!x->get_group().empty())
716 add_group_pair(x->get_group(),x);
723 Canvas::push_back_simple(etl::handle<Layer> x)
725 CanvasBase::insert(end(),x);
730 Canvas::erase(iterator iter)
732 if(!(*iter)->get_group().empty())
733 remove_group_pair((*iter)->get_group(),(*iter));
735 // HACK: We really shouldn't be wiping
736 // out these signals entirely. We should
737 // only be removing the specific connections
738 // that we made. At the moment, I'm too
739 // lazy to add the code to keep track
740 // of those connections, and no one else
741 // is using these signals, so I'll just
742 // leave these next two lines like they
743 // are for now - darco 07-30-2004
745 // so don't wipe them out entirely
746 // - dooglus 09-21-2007
747 disconnect_connections(*iter);
749 if(!op_flag_)remove_child(iter->get());
751 CanvasBase::erase(iter);
752 if(!op_flag_)changed();
756 Canvas::clone(const GUID& deriv_guid)const
763 name=get_id()+"_CLONE";
765 throw runtime_error("Cloning of non-inline canvases is not yet supported");
768 Handle canvas(new Canvas(name));
772 canvas->is_inline_=true;
773 // \todo this was setting parent_=0 - is there a reason for that?
774 // this was causing bug 1838132, where cloning an inline canvas that contains an imported image fails
775 // it was failing to ascertain the absolute pathname of the imported image, since it needs the pathname
776 // of the canvas to get that, which is stored in the parent canvas
777 canvas->parent_=parent();
778 //canvas->set_inline(parent());
781 canvas->set_guid(get_guid()^deriv_guid);
784 for(iter=begin();iter!=end();++iter)
786 Layer::Handle layer((*iter)->clone(deriv_guid));
789 assert(layer.count()==1);
791 canvas->push_back(layer);
792 if(!(layer.count()>1))
794 synfig::error("Canvas::clone(): Cloned layer insertion failure!");
795 synfig::error("Canvas::clone(): \tlayer.count()=%d",layer.count());
796 synfig::error("Canvas::clone(): \tlayer->get_name()=%s",layer->get_name().c_str());
797 synfig::error("Canvas::clone(): \tbefore size()=%d",presize);
798 synfig::error("Canvas::clone(): \tafter size()=%d",size());
800 assert(layer.count()>1);
804 synfig::error("Unable to clone layer");
808 canvas->signal_group_pair_removed().clear();
809 canvas->signal_group_pair_added().clear();
815 Canvas::set_inline(LooseHandle parent)
817 if(is_inline_ && parent_)
826 // Have the parent inherit all of the group stuff
828 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
830 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
832 parent->group_db_[iter->first].insert(iter->second.begin(),iter->second.end());
835 rend_desc()=parent->rend_desc();
839 Canvas::create_inline(Handle parent)
842 //if(parent->is_inline())
843 // return create_inline(parent->parent());
845 Handle canvas(new Canvas("inline"));
846 canvas->set_inline(parent);
851 Canvas::new_child_canvas()
853 if(is_inline() && parent_)
854 return parent_->new_child_canvas();
855 // runtime_error("You cannot create a child Canvas in an inline Canvas");
857 // Create a new canvas
858 children().push_back(create());
859 Canvas::Handle canvas(children().back());
861 canvas->parent_=this;
863 canvas->rend_desc()=rend_desc();
869 Canvas::new_child_canvas(const String &id)
871 if(is_inline() && parent_)
872 return parent_->new_child_canvas(id);
873 // runtime_error("You cannot create a child Canvas in an inline Canvas");
875 // Create a new canvas
876 children().push_back(create());
877 Canvas::Handle canvas(children().back());
880 canvas->parent_=this;
881 canvas->rend_desc()=rend_desc();
887 Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id)
889 if(is_inline() && parent_)
890 return parent_->add_child_canvas(child_canvas,id);
892 if(child_canvas->parent() && !child_canvas->is_inline())
893 throw std::runtime_error("Cannot add child canvas because it belongs to someone else!");
896 throw runtime_error("Invalid ID");
901 throw Exception::IDAlreadyExists(id);
903 catch(Exception::IDNotFound)
905 if(child_canvas->is_inline())
906 child_canvas->is_inline_=false;
907 child_canvas->id_=id;
908 children().push_back(child_canvas);
909 child_canvas->parent_=this;
916 Canvas::remove_child_canvas(Canvas::Handle child_canvas)
918 if(is_inline() && parent_)
919 return parent_->remove_child_canvas(child_canvas);
921 if(child_canvas->parent_!=this)
922 throw runtime_error("Given child does not belong to me");
924 if(find(children().begin(),children().end(),child_canvas)==children().end())
925 throw Exception::IDNotFound(child_canvas->get_id());
927 children().remove(child_canvas);
929 child_canvas->parent_=0;
933 Canvas::set_file_name(const String &file_name)
936 parent()->set_file_name(file_name);
939 file_name_=file_name;
940 signal_file_name_changed_();
945 Canvas::signal_file_name_changed()
948 return signal_file_name_changed();
950 return signal_file_name_changed_;
954 Canvas::get_file_name()const
957 return parent()->get_file_name();
962 Canvas::get_file_path()const
965 return parent()->get_file_path();
966 return dirname(file_name_);
971 Canvas::get_meta_data(const String& key)const
973 if(!meta_data_.count(key))
975 return meta_data_.find(key)->second;
979 Canvas::set_meta_data(const String& key, const String& data)
981 if(meta_data_[key]!=data)
983 meta_data_[key]=data;
984 signal_meta_data_changed()(key);
985 signal_meta_data_changed(key)();
990 Canvas::erase_meta_data(const String& key)
992 if(meta_data_.count(key))
994 meta_data_.erase(key);
995 signal_meta_data_changed()(key);
996 signal_meta_data_changed(key)();
1001 Canvas::get_meta_data_keys()const
1003 std::list<String> ret;
1005 std::map<String,String>::const_iterator iter;
1007 for(iter=meta_data_.begin();!(iter==meta_data_.end());++iter)
1008 ret.push_back(iter->first);
1013 /* note - the "Motion Blur" and "Duplicate" layers need the dynamic
1014 parameters of any PasteCanvas layers they loop over to be
1015 maintained. When the variables in the following function
1016 refer to "motion blur", they mean either of these two
1019 synfig::optimize_layers(Time time, Context context, Canvas::Handle op_canvas, bool seen_motion_blur_in_parent)
1023 std::vector< std::pair<float,Layer::Handle> > sort_list;
1024 int i, motion_blur_i=0; // motion_blur_i is for resolving which layer comes first in the event of a z_depth tie
1025 float motion_blur_z_depth=0; // the z_depth of the least deep motion blur layer in this context
1026 bool seen_motion_blur_locally = false;
1027 bool motion_blurred; // the final result - is this layer blurred or not?
1029 // If the parent didn't cause us to already be motion blurred,
1030 // check whether there's a motion blur in this context,
1031 // and if so, calculate its z_depth.
1032 if (!seen_motion_blur_in_parent)
1033 for(iter=context,i=0;*iter;iter++,i++)
1035 Layer::Handle layer=*iter;
1037 // If the layer isn't active, don't worry about it
1038 if(!layer->active())
1041 // Any layer with an amount of zero is implicitly disabled.
1042 ValueBase value(layer->get_param("amount"));
1043 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1046 if(layer->get_name()=="MotionBlur" || layer->get_name()=="duplicate")
1048 float z_depth(layer->get_z_depth()*1.0001+i);
1050 // If we've seen a motion blur before in this context...
1051 if (seen_motion_blur_locally)
1053 // ... then we're only interested in this one if it's less deep...
1054 if (z_depth < motion_blur_z_depth)
1056 motion_blur_z_depth = z_depth;
1060 // ... otherwise we're always interested in it.
1063 motion_blur_z_depth = z_depth;
1065 seen_motion_blur_locally = true;
1070 // Go ahead and start romping through the canvas to paste
1071 for(iter=context,i=0;*iter;iter++,i++)
1073 Layer::Handle layer=*iter;
1074 float z_depth(layer->get_z_depth()*1.0001+i);
1076 // If the layer isn't active, don't worry about it
1077 if(!layer->active())
1080 // Any layer with an amount of zero is implicitly disabled.
1081 ValueBase value(layer->get_param("amount"));
1082 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1085 // note: this used to include "&& paste_canvas->get_time_offset()==0", but then
1086 // time-shifted layers weren't being sorted by z-depth (bug #1806852)
1087 if(layer->get_name()=="PasteCanvas")
1089 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1091 // we need to blur the sub canvas if:
1092 // our parent is blurred,
1093 // or the child is lower than a local blur,
1094 // or the child is at the same z_depth as a local blur, but later in the context
1097 if (seen_motion_blur_in_parent) synfig::info("seen BLUR in parent\n");
1098 else if (seen_motion_blur_locally)
1099 if (z_depth > motion_blur_z_depth) synfig::info("paste is deeper than BLUR\n");
1100 else if (z_depth == motion_blur_z_depth) { synfig::info("paste is same depth as BLUR\n");
1101 if (i > motion_blur_i) synfig::info("paste is physically deeper than BLUR\n");
1102 else synfig::info("paste is less physically deep than BLUR\n");
1103 } else synfig::info("paste is less deep than BLUR\n");
1104 else synfig::info("no BLUR at all\n");
1107 motion_blurred = (seen_motion_blur_in_parent ||
1108 (seen_motion_blur_locally &&
1109 (z_depth > motion_blur_z_depth ||
1110 (z_depth == motion_blur_z_depth && i > motion_blur_i))));
1112 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1113 Canvas::Handle paste_sub_canvas = paste_canvas->get_sub_canvas();
1114 if(paste_sub_canvas)
1115 optimize_layers(time, paste_sub_canvas->get_context(),sub_canvas,motion_blurred);
1117 // \todo: uncommenting the following breaks the rendering of at least examples/backdrop.sifz quite severely
1118 // #define SYNFIG_OPTIMIZE_PASTE_CANVAS
1119 #ifdef SYNFIG_OPTIMIZE_PASTE_CANVAS
1120 Canvas::iterator sub_iter;
1122 // Determine if we can just remove the paste canvas altogether
1123 if (paste_canvas->get_blend_method() == Color::BLEND_COMPOSITE &&
1124 paste_canvas->get_amount() == 1.0f &&
1125 paste_canvas->get_zoom() == 0 &&
1126 paste_canvas->get_time_offset() == 0 &&
1127 paste_canvas->get_origin() == Point(0,0) )
1129 for(sub_iter=sub_canvas->begin();sub_iter!=sub_canvas->end();++sub_iter)
1131 Layer* layer=sub_iter->get();
1133 // any layers that deform end up breaking things
1134 // so do things the old way if we run into anything like this
1135 if(!dynamic_cast<Layer_NoDeform*>(layer))
1138 ValueBase value(layer->get_param("blend_method"));
1139 if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE)
1143 // It has turned out that we don't need a paste canvas
1144 // layer, so just go ahead and add all the layers onto
1145 // the current stack and be done with it
1146 while(sub_canvas->size())
1148 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,sub_canvas->front()));
1149 //op_canvas->push_back_simple(sub_canvas->front());
1150 sub_canvas->pop_front();
1156 #endif // SYNFIG_OPTIMIZE_PASTE_CANVAS
1158 Layer::Handle new_layer(Layer::create("PasteCanvas"));
1159 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(false);
1162 Layer::DynamicParamList dynamic_param_list(paste_canvas->dynamic_param_list());
1163 for(Layer::DynamicParamList::const_iterator iter(dynamic_param_list.begin()); iter != dynamic_param_list.end(); ++iter)
1164 new_layer->connect_dynamic_param(iter->first, iter->second);
1166 Layer::ParamList param_list(paste_canvas->get_param_list());
1167 //param_list.erase("canvas");
1168 new_layer->set_param_list(param_list);
1169 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_sub_canvas(sub_canvas);
1170 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(true);
1173 else // not a PasteCanvas - does it use blend method 'Straight'?
1175 /* when we use the 'straight' blend method, every pixel on the layer affects the layers underneath,
1176 * not just the non-transparent pixels; the following workarea wraps non-pastecanvas layers in a
1177 * new pastecanvas to ensure that the straight blend affects the full plane, not just the area
1178 * within the layer's bounding box
1181 // \todo: this code probably needs modification to work properly with motionblur and duplicate
1182 etl::handle<Layer_Composite> composite = etl::handle<Layer_Composite>::cast_dynamic(layer);
1184 /* some layers (such as circle) don't touch pixels that aren't
1185 * part of the circle, so they don't get blended correctly when
1186 * using a straight blend. so we encapsulate the circle, and the
1187 * encapsulation layer takes care of the transparent pixels
1188 * for us. if we do that for all layers, however, then the
1189 * distortion layers no longer work, since they have no
1190 * context to work on. the Layer::reads_context() method
1191 * returns true for layers which need to be able to see
1192 * their context. we can't encapsulate those.
1195 Color::is_straight(composite->get_blend_method()) &&
1196 !composite->reads_context())
1198 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1199 sub_canvas->push_back(composite = composite->clone());
1200 layer = Layer::create("PasteCanvas");
1201 composite->set_description(strprintf("Wrapped clone of '%s'", composite->get_non_empty_description().c_str()));
1202 layer->set_description(strprintf("PasteCanvas wrapper for '%s'", composite->get_non_empty_description().c_str()));
1203 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1204 paste_canvas->set_blend_method(composite->get_blend_method());
1205 paste_canvas->set_amount(composite->get_amount());
1206 sub_canvas->set_time(time); // region and outline don't calculate their bounding rects until their time is set
1207 composite->set_blend_method(Color::BLEND_STRAIGHT); // do this before calling set_sub_canvas(), but after set_time()
1208 composite->set_amount(1.0f); // after set_time()
1209 paste_canvas->set_sub_canvas(sub_canvas);
1213 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,layer));
1214 //op_canvas->push_back_simple(layer);
1218 stable_sort(sort_list.begin(),sort_list.end());
1219 std::vector< std::pair<float,Layer::Handle> >::iterator iter2;
1220 for(iter2=sort_list.begin();iter2!=sort_list.end();++iter2)
1221 op_canvas->push_back_simple(iter2->second);
1222 op_canvas->op_flag_=true;
1226 Canvas::get_times_vfunc(Node::time_set &set) const
1228 const_iterator i = begin(),
1231 for(; i != iend; ++i)
1233 const Node::time_set &tset = (*i)->get_times();
1234 set.insert(tset.begin(),tset.end());
1238 std::set<etl::handle<Layer> >
1239 Canvas::get_layers_in_group(const String&group)
1241 if(is_inline() && parent_)
1242 return parent_->get_layers_in_group(group);
1244 if(group_db_.count(group)==0)
1245 return std::set<etl::handle<Layer> >();
1246 return group_db_.find(group)->second;
1250 Canvas::get_groups()const
1252 if(is_inline() && parent_)
1253 return parent_->get_groups();
1255 std::set<String> ret;
1256 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
1257 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
1258 ret.insert(iter->first);
1263 Canvas::get_group_count()const
1265 if(is_inline() && parent_)
1266 return parent_->get_group_count();
1268 return group_db_.size();
1272 Canvas::add_group_pair(String group, etl::handle<Layer> layer)
1274 group_db_[group].insert(layer);
1275 if(group_db_[group].size()==1)
1276 signal_group_added()(group);
1278 signal_group_changed()(group);
1280 signal_group_pair_added()(group,layer);
1282 if(is_inline() && parent_)
1283 return parent_->add_group_pair(group,layer);
1287 Canvas::remove_group_pair(String group, etl::handle<Layer> layer)
1289 group_db_[group].erase(layer);
1291 signal_group_pair_removed()(group,layer);
1293 if(group_db_[group].empty())
1295 group_db_.erase(group);
1296 signal_group_removed()(group);
1299 signal_group_changed()(group);
1301 if(is_inline() && parent_)
1302 return parent_->remove_group_pair(group,layer);
1306 Canvas::add_connection(etl::loose_handle<Layer> layer, sigc::connection connection)
1308 connections_[layer].push_back(connection);
1312 Canvas::disconnect_connections(etl::loose_handle<Layer> layer)
1314 std::vector<sigc::connection>::iterator iter;
1315 for(iter=connections_[layer].begin();iter!=connections_[layer].end();++iter)
1317 connections_[layer].clear();
1321 Canvas::rename_group(const String&old_name,const String&new_name)
1323 if(is_inline() && parent_)
1324 return parent_->rename_group(old_name,new_name);
1327 std::map<String,std::set<etl::handle<Layer> > >::iterator iter;
1328 iter=group_db_.find(old_name);
1329 if(iter!=group_db_.end())
1330 for(++iter;iter!=group_db_.end() && iter->first.find(old_name)==0;iter=group_db_.find(old_name),++iter)
1332 String name(iter->first,old_name.size(),String::npos);
1334 rename_group(iter->first,name);
1338 std::set<etl::handle<Layer> > layers(get_layers_in_group(old_name));
1339 std::set<etl::handle<Layer> >::iterator iter;
1341 for(iter=layers.begin();iter!=layers.end();++iter)
1343 (*iter)->remove_from_group(old_name);
1344 (*iter)->add_to_group(new_name);