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, 2008 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 ======================================================= */
36 #include "exception.h"
39 #include "layer_pastecanvas.h"
40 #include "loadcanvas.h"
41 #include <sigc++/bind.h>
45 using namespace synfig;
49 namespace synfig { extern Canvas::Handle open_canvas(const String &filename, String &errors, String &warnings); };
51 /* === M A C R O S ========================================================= */
53 #define ALLOW_CLONE_NON_INLINE_CANVASES
61 synfig::error("%d canvases not yet deleted!",counter);
65 int _CanvasCounter::counter(0);
67 /* === G L O B A L S ======================================================= */
69 /* === P R O C E D U R E S ================================================= */
71 /* === M E T H O D S ======================================================= */
73 Canvas::Canvas(const String &id):
75 version_ (CURRENT_CANVAS_VERSION),
81 _CanvasCounter::counter++;
94 // we were having a crash where pastecanvas layers were still
95 // refering to a canvas after it had been destroyed; this code
96 // will stop the pastecanvas layers from refering to the canvas
97 // before the canvas is destroyed
99 // the set_sub_canvas(0) ends up deleting the parent-child link,
100 // which deletes the current element from the set we're iterating
101 // through, so we have to make sure we've incremented the iterator
102 // before we mess with the pastecanvas
103 std::set<Node*>::iterator iter = parent_set.begin();
104 while (iter != parent_set.end())
106 Layer_PasteCanvas* paste_canvas = dynamic_cast<Layer_PasteCanvas*>(*iter);
109 paste_canvas->set_sub_canvas(0);
111 warning("destroyed canvas has a parent that is not a pastecanvas - please report if repeatable");
114 //if(is_inline() && parent_) assert(0);
115 _CanvasCounter::counter--;
123 return CanvasBase::end()-1;
126 Canvas::const_iterator
129 return CanvasBase::end()-1;
132 Canvas::reverse_iterator
135 return CanvasBase::rbegin()+1;
138 Canvas::const_reverse_iterator
139 Canvas::rbegin()const
141 return CanvasBase::rbegin()+1;
147 return CanvasBase::size()-1;
155 Layer::Handle layer(front());
156 //if(layer->count()>2)synfig::info("before layer->count()=%d",layer->count());
159 //if(layer->count()>1)synfig::info("after layer->count()=%d",layer->count());
161 //CanvasBase::clear();
163 // We need to keep a blank handle at the
164 // end of the image list, and acts at
165 // the bottom. Without it, the layers
166 // would just continue going when polled
168 CanvasBase::push_back(Layer::Handle());
176 return CanvasBase::size()<=1;
182 return *(CanvasBase::end()-1);
185 const Layer::Handle &
188 return *(CanvasBase::end()-1);
192 Canvas::get_context()const
197 const ValueNodeList &
198 Canvas::value_node_list()const
200 if(is_inline() && parent_)
201 return parent_->value_node_list();
202 return value_node_list_;
206 Canvas::keyframe_list()
208 if(is_inline() && parent_)
209 return parent_->keyframe_list();
210 return keyframe_list_;
214 Canvas::keyframe_list()const
216 if(is_inline() && parent_)
217 return parent_->keyframe_list();
218 return keyframe_list_;
222 Canvas::find_layer(const Point &pos)
224 return get_context().hit_check(pos);
228 valid_id(const String &x)
230 static const char bad_chars[]=" :#@$^&()*";
233 if(!x.empty() && x[0]>='0' && x[0]<='9')
236 for(i=0;i<sizeof(bad_chars);i++)
237 if(x.find_first_of(bad_chars[i])!=string::npos)
244 Canvas::set_id(const String &x)
246 if(is_inline() && parent_)
247 throw runtime_error("Inline Canvas cannot have an ID");
250 throw runtime_error("Invalid ID");
252 signal_id_changed_();
256 Canvas::set_name(const String &x)
259 signal_meta_data_changed()("name");
260 signal_meta_data_changed("name")();
264 Canvas::set_author(const String &x)
267 signal_meta_data_changed()("author");
268 signal_meta_data_changed("author")();
272 Canvas::set_description(const String &x)
275 signal_meta_data_changed()("description");
276 signal_meta_data_changed("description")();
280 Canvas::set_time(Time t)const
282 if(is_dirty_ || !get_time().is_equal(t))
287 synfig::info("is_dirty_=%d",is_dirty_);
288 synfig::info("get_time()=%f",(float)get_time());
289 synfig::info("t=%f",(float)t);
294 const_cast<Canvas&>(*this).cur_time_=t;
297 get_context().set_time(t);
303 Canvas::get_root()const
305 return parent_?parent_->get_root().get():const_cast<synfig::Canvas *>(this);
309 Canvas::get_depth(etl::handle<Layer> layer)const
313 for(iter=begin();iter!=end();++iter,i++)
322 Canvas::get_relative_id(etl::loose_handle<const Canvas> x)const
324 if(x->get_root()==this)
326 if(is_inline() && parent_)
327 return parent_->_get_relative_id(x);
328 return _get_relative_id(x);
332 Canvas::_get_relative_id(etl::loose_handle<const Canvas> x)const
334 if(is_inline() && parent_)
335 return parent_->_get_relative_id(x);
340 if(parent()==x.get())
345 const Canvas* canvas=this;
347 for(;!canvas->is_root();canvas=canvas->parent().get())
348 id=':'+canvas->get_id()+id;
350 if(x && get_root()!=x->get_root())
352 //String file_name=get_file_name();
353 //String file_path=x->get_file_path();
356 if(is_absolute_path(get_file_name()))
357 file_name=etl::relative_path(x->get_file_path(),get_file_name());
359 file_name=get_file_name();
361 // If the path of X is inside of file_name,
363 //if(file_name.size()>file_path.size())
364 // if(file_path==String(file_name,0,file_path.size()))
365 // file_name.erase(0,file_path.size()+1);
374 Canvas::find_value_node(const String &id)
377 ValueNode::Handle::cast_const(
378 const_cast<const Canvas*>(this)->find_value_node(id)
382 ValueNode::ConstHandle
383 Canvas::find_value_node(const String &id)const
385 if(is_inline() && parent_)
386 return parent_->find_value_node(id);
389 throw Exception::IDNotFound("Empty ID");
391 // If we do not have any resolution, then we assume that the
392 // request is for this immediate canvas
393 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
394 return value_node_list_.find(id);
396 String canvas_id(id,0,id.rfind(':'));
397 String value_node_id(id,id.rfind(':')+1);
398 if(canvas_id.empty())
400 //synfig::warning("constfind:value_node_id: "+value_node_id);
401 //synfig::warning("constfind:canvas_id: "+canvas_id);
404 return find_canvas(canvas_id, warnings)->value_node_list_.find(value_node_id);
408 Canvas::surefind_value_node(const String &id)
410 if(is_inline() && parent_)
411 return parent_->surefind_value_node(id);
414 throw Exception::IDNotFound("Empty ID");
416 // If we do not have any resolution, then we assume that the
417 // request is for this immediate canvas
418 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
419 return value_node_list_.surefind(id);
421 String canvas_id(id,0,id.rfind(':'));
422 String value_node_id(id,id.rfind(':')+1);
423 if(canvas_id.empty())
427 return surefind_canvas(canvas_id,warnings)->value_node_list_.surefind(value_node_id);
431 Canvas::add_value_node(ValueNode::Handle x, const String &id)
433 if(is_inline() && parent_)
434 return parent_->add_value_node(x,id);
435 // throw runtime_error("You cannot add a ValueNode to an inline Canvas");
438 throw runtime_error("ValueNode is already exported");
441 throw Exception::BadLinkName("Empty ID");
443 if(id.find_first_of(':',0)!=string::npos)
444 throw Exception::BadLinkName("Bad character");
448 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
449 throw Exception::IDNotFound("add_value_node()");
451 throw Exception::IDAlreadyExists(id);
453 catch(Exception::IDNotFound)
457 x->set_parent_canvas(this);
459 if(!value_node_list_.add(x))
461 synfig::error("Unable to add ValueNode");
462 throw std::runtime_error("Unable to add ValueNode");
471 Canvas::rename_value_node(ValueNode::Handle x, const String &id)
474 throw Exception::BadLinkName("Empty ID");
476 if(id.find_first_of(": ",0)!=string::npos)
477 throw Exception::BadLinkName("Bad character");
481 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
482 throw Exception::IDNotFound("rename_value_node");
483 throw Exception::IDAlreadyExists(id);
485 catch(Exception::IDNotFound)
495 Canvas::remove_value_node(ValueNode::Handle x)
497 if(is_inline() && parent_)
498 return parent_->remove_value_node(x);
499 // throw Exception::IDNotFound("Canvas::remove_value_node() was called from an inline canvas");
502 throw Exception::IDNotFound("Canvas::remove_value_node() was passed empty handle");
504 if(!value_node_list_.erase(x))
505 throw Exception::IDNotFound("Canvas::remove_value_node(): ValueNode was not found inside of this canvas");
507 //x->set_parent_canvas(0);
513 Canvas::surefind_canvas(const String &id, String &warnings)
515 if(is_inline() && parent_)
516 return parent_->surefind_canvas(id,warnings);
521 // If the ID contains a "#" character, then a filename is
522 // expected on the left side.
523 if(id.find_first_of('#')!=string::npos)
525 // If '#' is the first character, remove it
526 // and attempt to parse the ID again.
528 return surefind_canvas(String(id,1),warnings);
530 //! \todo This needs a lot more optimization
531 String file_name(id,0,id.find_first_of('#'));
532 String external_id(id,id.find_first_of('#')+1);
534 file_name=unix_to_local_path(file_name);
536 Canvas::Handle external_canvas;
538 if(!is_absolute_path(file_name))
539 file_name = get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name;
541 // If the composition is already open, then use it.
542 if(externals_.count(file_name))
543 external_canvas=externals_[file_name];
547 external_canvas=open_canvas(file_name, errors, warnings);
549 throw runtime_error(errors);
550 externals_[file_name]=external_canvas;
553 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id, warnings));
556 // If we do not have any resolution, then we assume that the
557 // request is for this immediate canvas
558 if(id.find_first_of(':')==string::npos)
560 Children::iterator iter;
562 // Search for the image in the image list,
563 // and return it if it is found
564 for(iter=children().begin();iter!=children().end();iter++)
565 if(id==(*iter)->get_id())
568 // Create a new canvas and return it
569 //synfig::warning("Implicitly creating canvas named "+id);
570 return new_child_canvas(id);
573 // If the first character is the separator, then
574 // this references the root canvas.
576 return get_root()->surefind_canvas(string(id,1),warnings);
578 // Now we know that the requested Canvas is in a child
579 // of this canvas. We have to find that canvas and
580 // call "find_canvas" on it, and return the result.
582 String canvas_name=string(id,0,id.find_first_of(':'));
584 Canvas::Handle child_canvas=surefind_canvas(canvas_name,warnings);
586 return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1),warnings);
590 Canvas::find_canvas(const String &id, String &warnings)
593 Canvas::Handle::cast_const(
594 const_cast<const Canvas*>(this)->find_canvas(id, warnings)
599 Canvas::find_canvas(const String &id, String &warnings)const
601 if(is_inline() && parent_)
602 return parent_->find_canvas(id, warnings);
607 // If the ID contains a "#" character, then a filename is
608 // expected on the left side.
609 if(id.find_first_of('#')!=string::npos)
611 // If '#' is the first character, remove it
612 // and attempt to parse the ID again.
614 return find_canvas(String(id,1), warnings);
616 //! \todo This needs a lot more optimization
617 String file_name(id,0,id.find_first_of('#'));
618 String external_id(id,id.find_first_of('#')+1);
620 file_name=unix_to_local_path(file_name);
622 Canvas::Handle external_canvas;
624 if(!is_absolute_path(file_name))
625 file_name = get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name;
627 // If the composition is already open, then use it.
628 if(externals_.count(file_name))
629 external_canvas=externals_[file_name];
632 String errors, warnings;
633 external_canvas=open_canvas(file_name, errors, warnings);
635 throw runtime_error(errors);
636 externals_[file_name]=external_canvas;
639 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id, warnings));
642 // If we do not have any resolution, then we assume that the
643 // request is for this immediate canvas
644 if(id.find_first_of(':')==string::npos)
646 Children::const_iterator iter;
648 // Search for the image in the image list,
649 // and return it if it is found
650 for(iter=children().begin();iter!=children().end();iter++)
651 if(id==(*iter)->get_id())
654 throw Exception::IDNotFound("Child Canvas in Parent Canvas: (child)"+id);
657 // If the first character is the separator, then
658 // this references the root canvas.
660 return get_root()->find_canvas(string(id,1), warnings);
662 // Now we know that the requested Canvas is in a child
663 // of this canvas. We have to find that canvas and
664 // call "find_canvas" on it, and return the result.
666 String canvas_name=string(id,0,id.find_first_of(':'));
668 Canvas::ConstHandle child_canvas=find_canvas(canvas_name, warnings);
670 return child_canvas->find_canvas(string(id,id.find_first_of(':')+1), warnings);
676 return new Canvas("Untitled");
680 Canvas::push_back(etl::handle<Layer> x)
682 // int i(x->count());
684 //if(x->count()!=i+1)synfig::info("push_back before %d, after %d",i,x->count());
688 Canvas::push_front(etl::handle<Layer> x)
690 // int i(x->count());
692 //if(x->count()!=i+1)synfig::error("push_front before %d, after %d",i,x->count());
696 Canvas::insert(iterator iter,etl::handle<Layer> x)
698 // int i(x->count());
699 CanvasBase::insert(iter,x);
701 /*if(x->count()!=i+1)
703 synfig::error(__FILE__":%d: Canvas::insert(): ***FAILURE*** before %d, after %d",__LINE__,i,x->count());
705 //throw runtime_error("Canvas Insertion Failed");
712 LooseHandle correct_canvas(this);
713 //while(correct_canvas->is_inline())correct_canvas=correct_canvas->parent();
714 Layer::LooseHandle loose_layer(x);
716 add_connection(loose_layer,
717 sigc::connection::connection(
718 x->signal_added_to_group().connect(
722 &Canvas::add_group_pair),
724 add_connection(loose_layer,
725 sigc::connection::connection(
726 x->signal_removed_from_group().connect(
730 &Canvas::remove_group_pair),
733 if(!x->get_group().empty())
734 add_group_pair(x->get_group(),x);
740 Canvas::push_back_simple(etl::handle<Layer> x)
742 CanvasBase::insert(end(),x);
747 Canvas::erase(iterator iter)
749 if(!(*iter)->get_group().empty())
750 remove_group_pair((*iter)->get_group(),(*iter));
752 // HACK: We really shouldn't be wiping
753 // out these signals entirely. We should
754 // only be removing the specific connections
755 // that we made. At the moment, I'm too
756 // lazy to add the code to keep track
757 // of those connections, and no one else
758 // is using these signals, so I'll just
759 // leave these next two lines like they
760 // are for now - darco 07-30-2004
762 // so don't wipe them out entirely
763 // - dooglus 09-21-2007
764 disconnect_connections(*iter);
766 if(!op_flag_)remove_child(iter->get());
768 CanvasBase::erase(iter);
769 if(!op_flag_)changed();
773 Canvas::clone(const GUID& deriv_guid)const
780 name=get_id()+"_CLONE";
782 #ifndef ALLOW_CLONE_NON_INLINE_CANVASES
783 throw runtime_error("Cloning of non-inline canvases is not yet supported");
784 #endif // ALLOW_CLONE_NON_INLINE_CANVASES
787 Handle canvas(new Canvas(name));
791 canvas->is_inline_=true;
792 // \todo this was setting parent_=0 - is there a reason for that?
793 // this was causing bug 1838132, where cloning an inline canvas that contains an imported image fails
794 // it was failing to ascertain the absolute pathname of the imported image, since it needs the pathname
795 // of the canvas to get that, which is stored in the parent canvas
796 canvas->parent_=parent();
797 canvas->rend_desc() = rend_desc();
798 //canvas->set_inline(parent());
801 canvas->set_guid(get_guid()^deriv_guid);
804 for(iter=begin();iter!=end();++iter)
806 Layer::Handle layer((*iter)->clone(deriv_guid));
809 assert(layer.count()==1);
811 canvas->push_back(layer);
812 if(!(layer.count()>1))
814 synfig::error("Canvas::clone(): Cloned layer insertion failure!");
815 synfig::error("Canvas::clone(): \tlayer.count()=%d",layer.count());
816 synfig::error("Canvas::clone(): \tlayer->get_name()=%s",layer->get_name().c_str());
817 synfig::error("Canvas::clone(): \tbefore size()=%d",presize);
818 synfig::error("Canvas::clone(): \tafter size()=%d",size());
820 assert(layer.count()>1);
824 synfig::error("Unable to clone layer");
828 canvas->signal_group_pair_removed().clear();
829 canvas->signal_group_pair_added().clear();
835 Canvas::set_inline(LooseHandle parent)
837 if(is_inline_ && parent_)
846 // Have the parent inherit all of the group stuff
848 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
850 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
852 parent->group_db_[iter->first].insert(iter->second.begin(),iter->second.end());
855 rend_desc()=parent->rend_desc();
859 Canvas::create_inline(Handle parent)
862 //if(parent->is_inline())
863 // return create_inline(parent->parent());
865 Handle canvas(new Canvas(_("in line")));
866 canvas->set_inline(parent);
871 Canvas::new_child_canvas()
873 if(is_inline() && parent_)
874 return parent_->new_child_canvas();
875 // runtime_error("You cannot create a child Canvas in an inline Canvas");
877 // Create a new canvas
878 children().push_back(create());
879 Canvas::Handle canvas(children().back());
881 canvas->parent_=this;
883 canvas->rend_desc()=rend_desc();
889 Canvas::new_child_canvas(const String &id)
891 if(is_inline() && parent_)
892 return parent_->new_child_canvas(id);
893 // runtime_error("You cannot create a child Canvas in an inline Canvas");
895 // Create a new canvas
896 children().push_back(create());
897 Canvas::Handle canvas(children().back());
900 canvas->parent_=this;
901 canvas->rend_desc()=rend_desc();
907 Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id)
909 if(is_inline() && parent_)
910 return parent_->add_child_canvas(child_canvas,id);
912 if(child_canvas->parent() && !child_canvas->is_inline())
913 throw std::runtime_error("Cannot add child canvas because it belongs to someone else!");
916 throw runtime_error("Invalid ID");
921 find_canvas(id, warnings);
922 throw Exception::IDAlreadyExists(id);
924 catch(Exception::IDNotFound)
926 if(child_canvas->is_inline())
927 child_canvas->is_inline_=false;
928 child_canvas->id_=id;
929 children().push_back(child_canvas);
930 child_canvas->parent_=this;
937 Canvas::remove_child_canvas(Canvas::Handle child_canvas)
939 if(is_inline() && parent_)
940 return parent_->remove_child_canvas(child_canvas);
942 if(child_canvas->parent_!=this)
943 throw runtime_error("Given child does not belong to me");
945 if(find(children().begin(),children().end(),child_canvas)==children().end())
946 throw Exception::IDNotFound(child_canvas->get_id());
948 children().remove(child_canvas);
950 child_canvas->parent_=0;
954 Canvas::set_file_name(const String &file_name)
957 parent()->set_file_name(file_name);
960 String old_name(file_name_);
961 file_name_=file_name;
963 // when a canvas is made, its name is ""
964 // then, before it's saved or even edited, it gets a name like "Synfig Animation 23", in the local language
965 // we don't want to register the canvas' filename in the canvas map until it gets a real filename
968 file_name_=file_name;
969 std::map<synfig::String, etl::loose_handle<Canvas> >::iterator iter;
970 for(iter=get_open_canvas_map().begin();iter!=get_open_canvas_map().end();++iter)
971 if(iter->second==this)
973 if (iter == get_open_canvas_map().end())
974 CanvasParser::register_canvas_in_map(this, file_name);
976 signal_file_name_changed_();
982 Canvas::signal_file_name_changed()
985 return parent()->signal_file_name_changed();
987 return signal_file_name_changed_;
991 Canvas::get_file_name()const
994 return parent()->get_file_name();
999 Canvas::get_file_path()const
1002 return parent()->get_file_path();
1003 return dirname(file_name_);
1007 Canvas::get_meta_data(const String& key)const
1009 if(!meta_data_.count(key))
1011 return meta_data_.find(key)->second;
1015 Canvas::set_meta_data(const String& key, const String& data)
1017 if(meta_data_[key]!=data)
1019 meta_data_[key]=data;
1020 signal_meta_data_changed()(key);
1021 signal_meta_data_changed(key)();
1026 Canvas::erase_meta_data(const String& key)
1028 if(meta_data_.count(key))
1030 meta_data_.erase(key);
1031 signal_meta_data_changed()(key);
1032 signal_meta_data_changed(key)();
1037 Canvas::get_meta_data_keys()const
1039 std::list<String> ret;
1041 std::map<String,String>::const_iterator iter;
1043 for(iter=meta_data_.begin();!(iter==meta_data_.end());++iter)
1044 ret.push_back(iter->first);
1049 /* note - the "Motion Blur" and "Duplicate" layers need the dynamic
1050 parameters of any PasteCanvas layers they loop over to be
1051 maintained. When the variables in the following function
1052 refer to "motion blur", they mean either of these two
1055 synfig::optimize_layers(Time time, Context context, Canvas::Handle op_canvas, bool seen_motion_blur_in_parent)
1059 std::vector< std::pair<float,Layer::Handle> > sort_list;
1060 int i, motion_blur_i=0; // motion_blur_i is for resolving which layer comes first in the event of a z_depth tie
1061 float motion_blur_z_depth=0; // the z_depth of the least deep motion blur layer in this context
1062 bool seen_motion_blur_locally = false;
1063 bool motion_blurred; // the final result - is this layer blurred or not?
1065 // If the parent didn't cause us to already be motion blurred,
1066 // check whether there's a motion blur in this context,
1067 // and if so, calculate its z_depth.
1068 if (!seen_motion_blur_in_parent)
1069 for(iter=context,i=0;*iter;iter++,i++)
1071 Layer::Handle layer=*iter;
1073 // If the layer isn't active, don't worry about it
1074 if(!layer->active())
1077 // Any layer with an amount of zero is implicitly disabled.
1078 ValueBase value(layer->get_param("amount"));
1079 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1082 if(layer->get_name()=="MotionBlur" || layer->get_name()=="duplicate")
1084 float z_depth(layer->get_z_depth()*1.0001+i);
1086 // If we've seen a motion blur before in this context...
1087 if (seen_motion_blur_locally)
1089 // ... then we're only interested in this one if it's less deep...
1090 if (z_depth < motion_blur_z_depth)
1092 motion_blur_z_depth = z_depth;
1096 // ... otherwise we're always interested in it.
1099 motion_blur_z_depth = z_depth;
1101 seen_motion_blur_locally = true;
1106 // Go ahead and start romping through the canvas to paste
1107 for(iter=context,i=0;*iter;iter++,i++)
1109 Layer::Handle layer=*iter;
1110 float z_depth(layer->get_z_depth()*1.0001+i);
1112 // If the layer isn't active, don't worry about it
1113 if(!layer->active())
1116 // Any layer with an amount of zero is implicitly disabled.
1117 ValueBase value(layer->get_param("amount"));
1118 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1121 // note: this used to include "&& paste_canvas->get_time_offset()==0", but then
1122 // time-shifted layers weren't being sorted by z-depth (bug #1806852)
1123 if(layer->get_name()=="PasteCanvas")
1125 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1127 // we need to blur the sub canvas if:
1128 // our parent is blurred,
1129 // or the child is lower than a local blur,
1130 // or the child is at the same z_depth as a local blur, but later in the context
1133 if (seen_motion_blur_in_parent) synfig::info("seen BLUR in parent\n");
1134 else if (seen_motion_blur_locally)
1135 if (z_depth > motion_blur_z_depth) synfig::info("paste is deeper than BLUR\n");
1136 else if (z_depth == motion_blur_z_depth) { synfig::info("paste is same depth as BLUR\n");
1137 if (i > motion_blur_i) synfig::info("paste is physically deeper than BLUR\n");
1138 else synfig::info("paste is less physically deep than BLUR\n");
1139 } else synfig::info("paste is less deep than BLUR\n");
1140 else synfig::info("no BLUR at all\n");
1143 motion_blurred = (seen_motion_blur_in_parent ||
1144 (seen_motion_blur_locally &&
1145 (z_depth > motion_blur_z_depth ||
1146 (z_depth == motion_blur_z_depth && i > motion_blur_i))));
1148 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1149 Canvas::Handle paste_sub_canvas = paste_canvas->get_sub_canvas();
1150 if(paste_sub_canvas)
1151 optimize_layers(time, paste_sub_canvas->get_context(),sub_canvas,motion_blurred);
1153 // \todo: uncommenting the following breaks the rendering of at least examples/backdrop.sifz quite severely
1154 // #define SYNFIG_OPTIMIZE_PASTE_CANVAS
1155 #ifdef SYNFIG_OPTIMIZE_PASTE_CANVAS
1156 Canvas::iterator sub_iter;
1158 // Determine if we can just remove the paste canvas altogether
1159 if (paste_canvas->get_blend_method() == Color::BLEND_COMPOSITE &&
1160 paste_canvas->get_amount() == 1.0f &&
1161 paste_canvas->get_zoom() == 0 &&
1162 paste_canvas->get_time_offset() == 0 &&
1163 paste_canvas->get_origin() == Point(0,0) )
1165 for(sub_iter=sub_canvas->begin();sub_iter!=sub_canvas->end();++sub_iter)
1167 Layer* layer=sub_iter->get();
1169 // any layers that deform end up breaking things
1170 // so do things the old way if we run into anything like this
1171 if(!dynamic_cast<Layer_NoDeform*>(layer))
1174 ValueBase value(layer->get_param("blend_method"));
1175 if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE)
1179 // It has turned out that we don't need a paste canvas
1180 // layer, so just go ahead and add all the layers onto
1181 // the current stack and be done with it
1182 while(sub_canvas->size())
1184 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,sub_canvas->front()));
1185 //op_canvas->push_back_simple(sub_canvas->front());
1186 sub_canvas->pop_front();
1192 #endif // SYNFIG_OPTIMIZE_PASTE_CANVAS
1194 Layer::Handle new_layer(Layer::create("PasteCanvas"));
1195 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(false);
1198 Layer::DynamicParamList dynamic_param_list(paste_canvas->dynamic_param_list());
1199 for(Layer::DynamicParamList::const_iterator iter(dynamic_param_list.begin()); iter != dynamic_param_list.end(); ++iter)
1200 new_layer->connect_dynamic_param(iter->first, iter->second);
1202 Layer::ParamList param_list(paste_canvas->get_param_list());
1203 //param_list.erase("canvas");
1204 new_layer->set_param_list(param_list);
1205 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_sub_canvas(sub_canvas);
1206 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(true);
1209 else // not a PasteCanvas - does it use blend method 'Straight'?
1211 /* when we use the 'straight' blend method, every pixel on the layer affects the layers underneath,
1212 * not just the non-transparent pixels; the following workarea wraps non-pastecanvas layers in a
1213 * new pastecanvas to ensure that the straight blend affects the full plane, not just the area
1214 * within the layer's bounding box
1217 // \todo: this code probably needs modification to work properly with motionblur and duplicate
1218 etl::handle<Layer_Composite> composite = etl::handle<Layer_Composite>::cast_dynamic(layer);
1220 /* some layers (such as circle) don't touch pixels that aren't
1221 * part of the circle, so they don't get blended correctly when
1222 * using a straight blend. so we encapsulate the circle, and the
1223 * encapsulation layer takes care of the transparent pixels
1224 * for us. if we do that for all layers, however, then the
1225 * distortion layers no longer work, since they have no
1226 * context to work on. the Layer::reads_context() method
1227 * returns true for layers which need to be able to see
1228 * their context. we can't encapsulate those.
1231 Color::is_straight(composite->get_blend_method()) &&
1232 !composite->reads_context())
1234 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1235 // don't use clone() because it re-randomizes the seeds of any random valuenodes
1236 sub_canvas->push_back(composite = composite->simple_clone());
1237 layer = Layer::create("PasteCanvas");
1238 composite->set_description(strprintf("Wrapped clone of '%s'", composite->get_non_empty_description().c_str()));
1239 layer->set_description(strprintf("PasteCanvas wrapper for '%s'", composite->get_non_empty_description().c_str()));
1240 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1241 paste_canvas->set_blend_method(composite->get_blend_method());
1242 paste_canvas->set_amount(composite->get_amount());
1243 sub_canvas->set_time(time); // region and outline don't calculate their bounding rects until their time is set
1244 composite->set_blend_method(Color::BLEND_STRAIGHT); // do this before calling set_sub_canvas(), but after set_time()
1245 composite->set_amount(1.0f); // after set_time()
1246 paste_canvas->set_sub_canvas(sub_canvas);
1250 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,layer));
1251 //op_canvas->push_back_simple(layer);
1255 stable_sort(sort_list.begin(),sort_list.end());
1256 std::vector< std::pair<float,Layer::Handle> >::iterator iter2;
1257 for(iter2=sort_list.begin();iter2!=sort_list.end();++iter2)
1258 op_canvas->push_back_simple(iter2->second);
1259 op_canvas->op_flag_=true;
1263 Canvas::get_times_vfunc(Node::time_set &set) const
1265 const_iterator i = begin(),
1268 for(; i != iend; ++i)
1270 const Node::time_set &tset = (*i)->get_times();
1271 set.insert(tset.begin(),tset.end());
1275 std::set<etl::handle<Layer> >
1276 Canvas::get_layers_in_group(const String&group)
1278 if(is_inline() && parent_)
1279 return parent_->get_layers_in_group(group);
1281 if(group_db_.count(group)==0)
1282 return std::set<etl::handle<Layer> >();
1283 return group_db_.find(group)->second;
1287 Canvas::get_groups()const
1289 if(is_inline() && parent_)
1290 return parent_->get_groups();
1292 std::set<String> ret;
1293 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
1294 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
1295 ret.insert(iter->first);
1300 Canvas::get_group_count()const
1302 if(is_inline() && parent_)
1303 return parent_->get_group_count();
1305 return group_db_.size();
1309 Canvas::add_group_pair(String group, etl::handle<Layer> layer)
1311 group_db_[group].insert(layer);
1312 if(group_db_[group].size()==1)
1313 signal_group_added()(group);
1315 signal_group_changed()(group);
1317 signal_group_pair_added()(group,layer);
1319 if(is_inline() && parent_)
1320 return parent_->add_group_pair(group,layer);
1324 Canvas::remove_group_pair(String group, etl::handle<Layer> layer)
1326 group_db_[group].erase(layer);
1328 signal_group_pair_removed()(group,layer);
1330 if(group_db_[group].empty())
1332 group_db_.erase(group);
1333 signal_group_removed()(group);
1336 signal_group_changed()(group);
1338 if(is_inline() && parent_)
1339 return parent_->remove_group_pair(group,layer);
1343 Canvas::add_connection(etl::loose_handle<Layer> layer, sigc::connection connection)
1345 connections_[layer].push_back(connection);
1349 Canvas::disconnect_connections(etl::loose_handle<Layer> layer)
1351 std::vector<sigc::connection>::iterator iter;
1352 for(iter=connections_[layer].begin();iter!=connections_[layer].end();++iter)
1354 connections_[layer].clear();
1358 Canvas::rename_group(const String&old_name,const String&new_name)
1360 if(is_inline() && parent_)
1361 return parent_->rename_group(old_name,new_name);
1364 std::map<String,std::set<etl::handle<Layer> > >::iterator iter;
1365 iter=group_db_.find(old_name);
1366 if(iter!=group_db_.end())
1367 for(++iter;iter!=group_db_.end() && iter->first.find(old_name)==0;iter=group_db_.find(old_name),++iter)
1369 String name(iter->first,old_name.size(),String::npos);
1371 rename_group(iter->first,name);
1375 std::set<etl::handle<Layer> > layers(get_layers_in_group(old_name));
1376 std::set<etl::handle<Layer> >::iterator iter;
1378 for(iter=layers.begin();iter!=layers.end();++iter)
1380 (*iter)->remove_from_group(old_name);
1381 (*iter)->add_to_group(new_name);
1386 Canvas::register_external_canvas(String file_name, Handle canvas)
1388 if(!is_absolute_path(file_name)) file_name = get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name;
1389 externals_[file_name] = canvas;
1394 Canvas::show_externals(String file, int line, String text) const
1396 printf(" .----- (externals for %lx '%s')\n | %s:%d %s\n", ulong(this), get_name().c_str(), file.c_str(), line, text.c_str());
1397 std::map<String, Handle>::iterator iter;
1398 for (iter = externals_.begin(); iter != externals_.end(); iter++)
1400 synfig::String first(iter->first);
1401 etl::loose_handle<Canvas> second(iter->second);
1402 printf(" | %40s : %lx (%d)\n", first.c_str(), ulong(&*second), second->count());
1404 printf(" `-----\n\n");