X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Ftrunk%2Fsrc%2Fsynfig%2Fcanvas.cpp;h=2bbb743366aed661dfc3dde7956999c420edee08;hb=9459638ad6797b8139f1e9f0715c96076dbf0890;hp=9322d79c683ea75cf12f692dec8ba76fb10fa11b;hpb=36b62b6ea64fe871356d77e7aac1f53b9b33cebb;p=synfig.git diff --git a/synfig-core/trunk/src/synfig/canvas.cpp b/synfig-core/trunk/src/synfig/canvas.cpp index 9322d79..2bbb743 100644 --- a/synfig-core/trunk/src/synfig/canvas.cpp +++ b/synfig-core/trunk/src/synfig/canvas.cpp @@ -6,7 +6,7 @@ ** ** \legal ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley -** Copyright (c) 2007 Chris Moore +** Copyright (c) 2007, 2008 Chris Moore ** ** This package is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as @@ -39,6 +39,7 @@ #include "time.h" #include "context.h" #include "layer_pastecanvas.h" +#include "loadcanvas.h" #include #endif @@ -47,10 +48,12 @@ using namespace synfig; using namespace etl; using namespace std; -namespace synfig { extern Canvas::Handle open_canvas(const String &filename); }; +namespace synfig { extern Canvas::Handle open_canvas(const String &filename, String &errors, String &warnings); }; /* === M A C R O S ========================================================= */ +#define ALLOW_CLONE_NON_INLINE_CANVASES + struct _CanvasCounter { static int counter; @@ -90,9 +93,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(); } @@ -350,7 +372,6 @@ Canvas::_get_relative_id(etl::loose_handle x)const return id; } - ValueNode::Handle Canvas::find_value_node(const String &id) { @@ -381,7 +402,8 @@ Canvas::find_value_node(const String &id)const //synfig::warning("constfind:value_node_id: "+value_node_id); //synfig::warning("constfind:canvas_id: "+canvas_id); - return find_canvas(canvas_id)->value_node_list_.find(value_node_id); + String warnings; + return find_canvas(canvas_id, warnings)->value_node_list_.find(value_node_id); } ValueNode::Handle @@ -403,7 +425,8 @@ Canvas::surefind_value_node(const String &id) if(canvas_id.empty()) canvas_id=':'; - return surefind_canvas(canvas_id)->value_node_list_.surefind(value_node_id); + String warnings; + return surefind_canvas(canvas_id,warnings)->value_node_list_.surefind(value_node_id); } void @@ -413,7 +436,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"); @@ -425,16 +447,13 @@ Canvas::add_value_node(ValueNode::Handle x, const String &id) 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); @@ -444,7 +463,6 @@ Canvas::add_value_node(ValueNode::Handle x, const String &id) synfig::error("Unable to add ValueNode"); throw std::runtime_error("Unable to add ValueNode"); } - //DEBUGPOINT(); return; } @@ -493,12 +511,11 @@ Canvas::remove_value_node(ValueNode::Handle x) x->set_id(""); } - -etl::handle -Canvas::surefind_canvas(const String &id) +Canvas::Handle +Canvas::surefind_canvas(const String &id, String &warnings) { if(is_inline() && parent_) - return parent_->surefind_canvas(id); + return parent_->surefind_canvas(id,warnings); if(id.empty()) return this; @@ -510,7 +527,7 @@ Canvas::surefind_canvas(const String &id) // If '#' is the first character, remove it // and attempt to parse the ID again. if(id[0]=='#') - return surefind_canvas(String(id,1)); + return surefind_canvas(String(id,1),warnings); //! \todo This needs a lot more optimization String file_name(id,0,id.find_first_of('#')); @@ -520,22 +537,22 @@ Canvas::surefind_canvas(const String &id) Canvas::Handle external_canvas; + if(!is_absolute_path(file_name)) + file_name = get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name; + // If the composition is already open, then use it. if(externals_.count(file_name)) external_canvas=externals_[file_name]; else { - if(is_absolute_path(file_name)) - external_canvas=open_canvas(file_name); - else - external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name); - + String errors; + external_canvas=open_canvas(file_name, errors, warnings); if(!external_canvas) - throw Exception::FileNotFound(file_name); + throw runtime_error(errors); externals_[file_name]=external_canvas; } - return Handle::cast_const(external_canvas.constant()->find_canvas(external_id)); + return Handle::cast_const(external_canvas.constant()->find_canvas(external_id, warnings)); } // If we do not have any resolution, then we assume that the @@ -558,7 +575,7 @@ Canvas::surefind_canvas(const String &id) // If the first character is the separator, then // this references the root canvas. if(id[0]==':') - return get_root()->surefind_canvas(string(id,1)); + return get_root()->surefind_canvas(string(id,1),warnings); // Now we know that the requested Canvas is in a child // of this canvas. We have to find that canvas and @@ -566,24 +583,25 @@ Canvas::surefind_canvas(const String &id) String canvas_name=string(id,0,id.find_first_of(':')); - Canvas::Handle child_canvas=surefind_canvas(canvas_name); + Canvas::Handle child_canvas=surefind_canvas(canvas_name,warnings); - return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1)); + return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1),warnings); } Canvas::Handle -Canvas::find_canvas(const String &id) +Canvas::find_canvas(const String &id, String &warnings) { return Canvas::Handle::cast_const( - const_cast(this)->find_canvas(id) + const_cast(this)->find_canvas(id, warnings) ); } Canvas::ConstHandle -Canvas::find_canvas(const String &id)const +Canvas::find_canvas(const String &id, String &warnings)const { - if(is_inline() && parent_)return parent_->find_canvas(id); + if(is_inline() && parent_) + return parent_->find_canvas(id, warnings); if(id.empty()) return this; @@ -595,7 +613,7 @@ Canvas::find_canvas(const String &id)const // If '#' is the first character, remove it // and attempt to parse the ID again. if(id[0]=='#') - return find_canvas(String(id,1)); + return find_canvas(String(id,1), warnings); //! \todo This needs a lot more optimization String file_name(id,0,id.find_first_of('#')); @@ -605,22 +623,22 @@ Canvas::find_canvas(const String &id)const Canvas::Handle external_canvas; + if(!is_absolute_path(file_name)) + file_name = get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name; + // If the composition is already open, then use it. if(externals_.count(file_name)) external_canvas=externals_[file_name]; else { - if(is_absolute_path(file_name)) - external_canvas=open_canvas(file_name); - else - external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name); - + String errors, warnings; + external_canvas=open_canvas(file_name, errors, warnings); if(!external_canvas) - throw Exception::FileNotFound(file_name); + throw runtime_error(errors); externals_[file_name]=external_canvas; } - return Handle::cast_const(external_canvas.constant()->find_canvas(external_id)); + return Handle::cast_const(external_canvas.constant()->find_canvas(external_id, warnings)); } // If we do not have any resolution, then we assume that the @@ -640,8 +658,8 @@ Canvas::find_canvas(const String &id)const // 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)); + if(id[0]==':') + return get_root()->find_canvas(string(id,1), warnings); // Now we know that the requested Canvas is in a child // of this canvas. We have to find that canvas and @@ -649,12 +667,11 @@ Canvas::find_canvas(const String &id)const String canvas_name=string(id,0,id.find_first_of(':')); - Canvas::ConstHandle child_canvas=find_canvas(canvas_name); + Canvas::ConstHandle child_canvas=find_canvas(canvas_name, warnings); - return child_canvas->find_canvas(string(id,id.find_first_of(':')+1)); + return child_canvas->find_canvas(string(id,id.find_first_of(':')+1), warnings); } - Canvas::Handle Canvas::create() { @@ -664,7 +681,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()); @@ -673,7 +689,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()); @@ -694,10 +709,8 @@ Canvas::insert(iterator iter,etl::handle x) x->set_canvas(this); - add_child(x.get()); - LooseHandle correct_canvas(this); //while(correct_canvas->is_inline())correct_canvas=correct_canvas->parent(); Layer::LooseHandle loose_layer(x); @@ -719,11 +732,9 @@ Canvas::insert(iterator iter,etl::handle x) &Canvas::remove_group_pair), loose_layer)))); - if(!x->get_group().empty()) add_group_pair(x->get_group(),x); - changed(); } @@ -770,7 +781,9 @@ Canvas::clone(const GUID& deriv_guid)const { name=get_id()+"_CLONE"; +#ifndef ALLOW_CLONE_NON_INLINE_CANVASES throw runtime_error("Cloning of non-inline canvases is not yet supported"); +#endif // ALLOW_CLONE_NON_INLINE_CANVASES } Handle canvas(new Canvas(name)); @@ -783,6 +796,7 @@ Canvas::clone(const GUID& deriv_guid)const // 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->rend_desc() = rend_desc(); //canvas->set_inline(parent()); } @@ -905,7 +919,8 @@ Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id) try { - find_canvas(id); + String warnings; + find_canvas(id, warnings); throw Exception::IDAlreadyExists(id); } catch(Exception::IDNotFound) @@ -944,8 +959,24 @@ Canvas::set_file_name(const String &file_name) parent()->set_file_name(file_name); else { + String old_name(file_name_); file_name_=file_name; - signal_file_name_changed_(); + + // when a canvas is made, its name is "" + // then, before it's saved or even edited, it gets a name like "Synfig Animation 23", in the local language + // we don't want to register the canvas' filename in the canvas map until it gets a real filename + if (old_name != "") + { + file_name_=file_name; + std::map >::iterator iter; + for(iter=get_open_canvas_map().begin();iter!=get_open_canvas_map().end();++iter) + if(iter->second==this) + break; + if (iter == get_open_canvas_map().end()) + CanvasParser::register_canvas_in_map(this, file_name); + else + signal_file_name_changed_(); + } } } @@ -953,7 +984,7 @@ sigc::signal& Canvas::signal_file_name_changed() { if(parent()) - return signal_file_name_changed(); + return parent()->signal_file_name_changed(); else return signal_file_name_changed_; } @@ -974,7 +1005,6 @@ Canvas::get_file_path()const return dirname(file_name_); } - String Canvas::get_meta_data(const String& key)const { @@ -1024,7 +1054,7 @@ Canvas::get_meta_data_keys()const refer to "motion blur", they mean either of these two layers. */ void -synfig::optimize_layers(Context context, Canvas::Handle op_canvas, bool seen_motion_blur_in_parent) +synfig::optimize_layers(Time time, Context context, Canvas::Handle op_canvas, bool seen_motion_blur_in_parent) { Context iter; @@ -1090,12 +1120,12 @@ synfig::optimize_layers(Context context, Canvas::Handle op_canvas, bool seen_mot if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0) continue; - Layer_PasteCanvas* paste_canvas(static_cast(layer.get())); - // 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, @@ -1120,41 +1150,49 @@ synfig::optimize_layers(Context context, Canvas::Handle op_canvas, bool seen_mot Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas)); Canvas::Handle paste_sub_canvas = paste_canvas->get_sub_canvas(); if(paste_sub_canvas) - optimize_layers(paste_sub_canvas->get_context(),sub_canvas,motion_blurred); -//#define SYNFIG_OPTIMIZE_PASTE_CANVAS 1 + optimize_layers(time, paste_sub_canvas->get_context(),sub_canvas,motion_blurred); +// \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(); + // 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(); - ValueBase value(layer->get_param("blend_method")); - if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE) - throw int(); - } + // 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(); - // 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(); + 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_muck_with_time(false); if (motion_blurred) @@ -1170,6 +1208,46 @@ synfig::optimize_layers(Context context, Canvas::Handle op_canvas, bool seen_mot 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)); + // don't use clone() because it re-randomizes the seeds of any random valuenodes + sub_canvas->push_back(composite = composite->simple_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); @@ -1305,3 +1383,26 @@ Canvas::rename_group(const String&old_name,const String&new_name) (*iter)->add_to_group(new_name); } } + +void +Canvas::register_external_canvas(String file_name, Handle canvas) +{ + if(!is_absolute_path(file_name)) file_name = get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name; + externals_[file_name] = canvas; +} + +#ifdef _DEBUG +void +Canvas::show_externals(String file, int line, String text) const +{ + printf(" .----- (externals for %lx '%s')\n | %s:%d %s\n", ulong(this), get_name().c_str(), file.c_str(), line, text.c_str()); + std::map::iterator iter; + for (iter = externals_.begin(); iter != externals_.end(); iter++) + { + synfig::String first(iter->first); + etl::loose_handle second(iter->second); + printf(" | %40s : %lx (%d)\n", first.c_str(), ulong(&*second), second->count()); + } + printf(" `-----\n\n"); +} +#endif // _DEBUG