From 0b6b395ee6f16e42bf36e691c91af83fa95d97e1 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Fri, 1 Nov 2019 14:50:57 +0100 Subject: Improve clip LPE Fixes: https://gitlab.com/inkscape/inbox/issues/868 https://gitlab.com/inkscape/inbox/issues/867 https://gitlab.com/inkscape/inbox/issues/866 --- src/live_effects/lpe-powerclip.cpp | 504 +-- src/live_effects/lpe-powerclip.h | 22 +- src/object/sp-item.cpp | 8 +- src/object/sp-item.h | 6 +- src/verbs.cpp | 9 +- testfiles/rendering_tests/CMakeLists.txt | 2 + testfiles/rendering_tests/README | 8 +- .../expected_rendering/test-lpe-0.92-large.png | Bin 0 -> 199059 bytes .../expected_rendering/test-lpe-0.92.png | Bin 0 -> 42622 bytes .../expected_rendering/test-lpe-1.0-large.png | Bin 0 -> 225290 bytes .../expected_rendering/test-lpe-1.0.png | Bin 0 -> 48377 bytes testfiles/rendering_tests/test-lpe-0.92.svg | 4034 +++++++++++++++++++ testfiles/rendering_tests/test-lpe-1.0.svg | 4247 ++++++++++++++++++++ 13 files changed, 8516 insertions(+), 324 deletions(-) create mode 100644 testfiles/rendering_tests/expected_rendering/test-lpe-0.92-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-lpe-0.92.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-lpe-1.0-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-lpe-1.0.png create mode 100644 testfiles/rendering_tests/test-lpe-0.92.svg create mode 100644 testfiles/rendering_tests/test-lpe-1.0.svg diff --git a/src/live_effects/lpe-powerclip.cpp b/src/live_effects/lpe-powerclip.cpp index 7e3f8edb7..250c4c8ca 100644 --- a/src/live_effects/lpe-powerclip.cpp +++ b/src/live_effects/lpe-powerclip.cpp @@ -3,24 +3,21 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include "live_effects/lpe-powerclip.h" -#include "live_effects/lpeobject.h" -#include "live_effects/lpeobject-reference.h" -#include <2geom/path-intersection.h> -#include <2geom/intersection-graph.h> #include "display/curve.h" -#include "helper/geom.h" -#include "path-chemistry.h" -#include "extract-uri.h" -#include - +#include "live_effects/lpeobject-reference.h" +#include "live_effects/lpeobject.h" #include "object/sp-clippath.h" +#include "object/sp-defs.h" +#include "object/sp-item-group.h" +#include "object/sp-item.h" #include "object/sp-path.h" #include "object/sp-shape.h" -#include "object/sp-item.h" -#include "object/sp-item-group.h" #include "object/sp-use.h" -#include "object/uri.h" +#include "style.h" +#include "svg/svg.h" +#include <2geom/intersection-graph.h> +#include <2geom/path-intersection.h> // TODO due to internal breakage in glibmm headers, this must be last: #include @@ -28,264 +25,218 @@ namespace Inkscape { namespace LivePathEffect { LPEPowerClip::LPEPowerClip(LivePathEffectObject *lpeobject) - : Effect(lpeobject), - hide_clip(_("Hide clip"), _("Hide clip"), "hide_clip", &wr, this, false), - is_inverse("Store the last inverse apply", "", "is_inverse", &wr, this, "false", false), - uri("Store the uri of clip", "", "uri", &wr, this, "false", false), - inverse(_("Inverse clip"), _("Inverse clip"), "inverse", &wr, this, false), - flatten(_("Flatten clip"), _("Flatten clip, see fill rule once convert to paths"), "flatten", &wr, this, false), - message(_("Info Box"), _("Important messages"), "message", &wr, this, _("Use fill-rule evenodd on fill and stroke dialog if no flatten result after convert clip to paths.")) + : Effect(lpeobject) + , hide_clip(_("Hide clip"), _("Hide clip"), "hide_clip", &wr, this, false) + , inverse(_("Inverse clip"), _("Inverse clip"), "inverse", &wr, this, true) + , flatten(_("Flatten clip"), _("Flatten clip, see fill rule once convert to paths"), "flatten", &wr, this, false) + , message( + _("Info Box"), _("Important messages"), "message", &wr, this, + _("Use fill-rule evenodd on fill and stroke dialog if no flatten result after convert clip to paths.")) { - registerParameter(&uri); registerParameter(&inverse); registerParameter(&flatten); registerParameter(&hide_clip); - registerParameter(&is_inverse); registerParameter(&message); message.param_set_min_height(55); + _updating = false; + _legacy = false; + // legazy fix between 0.92.4 launch and 1.0beta1 + if (this->getRepr()->attribute("is_inverse")) { + this->getRepr()->setAttribute("is_inverse", nullptr); + _legacy = true; + } } LPEPowerClip::~LPEPowerClip() = default; -void -LPEPowerClip::doOnApply (SPLPEItem const * lpeitem) +Geom::Path sp_bbox_without_clip(SPLPEItem *lpeitem) { - SPLPEItem * item = const_cast(lpeitem); - SPObject * clip_path = item->clip_ref->getObject(); - if (!clip_path) { - item->removeCurrentPathEffect(false); + Geom::OptRect bbox = lpeitem->visualBounds(Geom::identity(), true, false, true); + if (bbox) { + (*bbox).expandBy(5); + return Geom::Path(*bbox); } + return Geom::Path(); } -void -LPEPowerClip::doBeforeEffect (SPLPEItem const* lpeitem){ - SPObject * clip_path = SP_ITEM(sp_lpe_item)->clip_ref->getObject(); - gchar * uri_str = uri.param_getSVGValue(); - if(hide_clip && clip_path) { - SP_ITEM(sp_lpe_item)->clip_ref->detach(); - } else if (!hide_clip && !clip_path && uri_str) { - try { - SP_ITEM(sp_lpe_item)->clip_ref->attach(Inkscape::URI(uri_str)); - } catch (Inkscape::BadURIException &e) { - g_warning("%s", e.what()); - SP_ITEM(sp_lpe_item)->clip_ref->detach(); +Geom::PathVector sp_get_recursive_pathvector(SPLPEItem *item, Geom::PathVector res, bool dir, bool inverse) +{ + SPGroup *group = dynamic_cast(item); + if (group) { + std::vector item_list = sp_item_group_item_list(group); + for (auto child : item_list) { + if (child) { + SPLPEItem *childitem = dynamic_cast(child); + if (childitem) { + res = sp_get_recursive_pathvector(childitem, res, dir, inverse); + } + } } } - clip_path = SP_ITEM(sp_lpe_item)->clip_ref->getObject(); - if (clip_path) { - uri.param_setValue(Glib::ustring(extract_uri(sp_lpe_item->getRepr()->attribute("clip-path"))), true); - SP_ITEM(sp_lpe_item)->clip_ref->detach(); - Geom::OptRect bbox = sp_lpe_item->visualBounds(); - if(!bbox) { - return; - } - uri_str = uri.param_getSVGValue(); - if (uri_str) { - try { - SP_ITEM(sp_lpe_item)->clip_ref->attach(Inkscape::URI(uri_str)); - } catch (Inkscape::BadURIException &e) { - g_warning("%s", e.what()); - SP_ITEM(sp_lpe_item)->clip_ref->detach(); + SPShape *shape = dynamic_cast(item); + if (shape && shape->getCurve()) { + for (auto path : shape->getCurve()->get_pathvector()) { + if (!path.empty()) { + bool pathdir = Geom::path_direction(path); + if (pathdir == dir && inverse) { + path = path.reversed(); + } + res.push_back(path); } - } else { - SP_ITEM(sp_lpe_item)->clip_ref->detach(); } - g_free(uri_str); - Geom::Rect bboxrect = (*bbox); - bboxrect.expandBy(1); - Geom::Point topleft = bboxrect.corner(0); - Geom::Point topright = bboxrect.corner(1); - Geom::Point bottomright = bboxrect.corner(2); - Geom::Point bottomleft = bboxrect.corner(3); - clip_box.clear(); - clip_box.start(topleft); - clip_box.appendNew(topright); - clip_box.appendNew(bottomright); - clip_box.appendNew(bottomleft); - clip_box.close(); + } + return res; +} +Geom::PathVector LPEPowerClip::getClipPathvector() +{ + Geom::PathVector res; + Geom::PathVector res_hlp; + if (!sp_lpe_item) { + return res; + } + + SPObject *clip_path = sp_lpe_item->clip_ref->getObject(); + if (clip_path) { std::vector clip_path_list = clip_path->childList(true); - for ( std::vector::const_iterator iter=clip_path_list.begin();iter!=clip_path_list.end();++iter) { - SPObject * clip_data = *iter; - gchar * is_inverse_str = is_inverse.param_getSVGValue(); - if(!strcmp(is_inverse_str,"false") && inverse && isVisible()) { - SPCurve * clipcurve = new SPCurve(); - addInverse(SP_ITEM(clip_data), clipcurve, Geom::Affine::identity(), true); - clipcurve->unref(); - } else if((!strcmp(is_inverse_str,"true") && !inverse && isVisible()) || - (inverse && !is_visible && is_inverse_str == (Glib::ustring)"true")) - { - removeInverse(SP_ITEM(clip_data)); - } else if(inverse && isVisible()) { - updateInverse(SP_ITEM(clip_data)); + clip_path_list.pop_back(); + if (clip_path_list.size()) { + for (auto clip : clip_path_list) { + SPLPEItem *childitem = dynamic_cast(clip); + if (childitem) { + res_hlp = sp_get_recursive_pathvector(childitem, res_hlp, false, inverse); + if (is_load && _legacy) { + childitem->doWriteTransform(Geom::Translate(0, -999999)); + } + if (!childitem->style || !childitem->style->display.set || + childitem->style->display.value != SP_CSS_DISPLAY_NONE) { + childitem->style->display.set = TRUE; + childitem->style->display.value = SP_CSS_DISPLAY_NONE; + childitem->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } + } + } + if (is_load && _legacy) { + res_hlp *= Geom::Translate(0, -999999); + _legacy = false; } - g_free(is_inverse_str); } - } else if(!hide_clip) { - SPLPEItem * item = const_cast(lpeitem); - item->removeCurrentPathEffect(false); } -} - -void -LPEPowerClip::doAfterEffect (SPLPEItem const* lpeitem){ - is_load = false; - if (!hide_clip && flatten && isVisible()) { - SP_ITEM(sp_lpe_item)->clip_ref->detach(); + Geom::Path bbox = sp_bbox_without_clip(sp_lpe_item); + if (hide_clip) { + return bbox; + } + if (inverse && isVisible()) { + res.push_back(bbox); + } + for (auto path : res_hlp) { + res.push_back(path); } + return res; } -void -LPEPowerClip::addInverse (SPItem * clip_data, SPCurve * clipcurve, Geom::Affine affine, bool root){ - gchar * is_inverse_str = is_inverse.param_getSVGValue(); +void LPEPowerClip::add() +{ SPDocument *document = getSPDoc(); - if (!document) { + if (!document || !sp_lpe_item) { return; } + SPObject *clip_path = sp_lpe_item->clip_ref->getObject(); SPObject *elemref = NULL; - if(root) { + if (clip_path) { Inkscape::XML::Document *xml_doc = document->getReprDoc(); - SP_LPE_ITEM(clip_data)->removeAllPathEffects(true); + Inkscape::XML::Node *parent = clip_path->getRepr(); + SPLPEItem *childitem = dynamic_cast(clip_path->childList(true).back()); + if (childitem) { + if (const gchar *powerclip = childitem->getRepr()->attribute("class")) { + if (!strcmp(powerclip, "powerclip")) { + Glib::ustring newclip = Glib::ustring("clipath_") + getId(); + Glib::ustring uri = Glib::ustring("url(#") + newclip + Glib::ustring(")"); + parent = clip_path->getRepr()->duplicate(xml_doc); + parent->setAttribute("id", newclip.c_str()); + Inkscape::XML::Node *defs = clip_path->getRepr()->parent(); + clip_path = SP_OBJECT(document->getDefs()->appendChildRepr(parent)); + Inkscape::GC::release(parent); + sp_lpe_item->setAttribute("clip-path", uri.c_str()); + SPLPEItem *childitemdel = dynamic_cast(clip_path->childList(true).back()); + if (childitemdel) { + childitemdel->setAttribute("id", getId().c_str()); + return; + } + } + } + } Inkscape::XML::Node *clip_path_node = xml_doc->createElement("svg:path"); - Inkscape::XML::Node *parent = clip_data->getRepr()->parent(); parent->appendChild(clip_path_node); + Inkscape::GC::release(clip_path_node); elemref = document->getObjectByRepr(clip_path_node); - elemref->setAttribute("style","fill-rule:evenodd"); - elemref->setAttribute("id", (Glib::ustring("lpe_") + Glib::ustring(this->getLPEObj()->getId())).c_str()); - } - if(!strcmp(is_inverse_str,"false")) { - if (SP_IS_GROUP(clip_data)) { - std::vector item_list = sp_item_group_item_list(SP_GROUP(clip_data)); - for ( std::vector::const_iterator iter=item_list.begin();iter!=item_list.end();++iter) { - SPItem *subitem = *iter; - Geom::Affine affine_group = SP_ITEM(clip_data)->transform; - addInverse(subitem, clipcurve, affine_group, false); - if (root) { - clip_box *= affine_group; - } - } - } else if (SP_IS_SHAPE(clip_data)) { - SPCurve * c = nullptr; - c = SP_SHAPE(clip_data)->getCurve(); - c->transform(affine); - if (c) { - Geom::PathVector c_pv = c->get_pathvector(); - //TODO: this can be not correct but no better way - bool dir_a = Geom::path_direction(c_pv[0]); - bool dir_b = Geom::path_direction(clip_box); - if (dir_a == dir_b) { - clip_box = clip_box.reversed(); - } - clipcurve->append(c, false); - gchar * is_inverse_str = is_inverse.param_getSVGValue(); - if (strcmp(is_inverse_str, "true") != 0) { - is_inverse.param_setValue((Glib::ustring)"true", true); - } - c->reset(); - delete c; - } - Geom::Affine hidetrans = (Geom::Affine)Geom::Translate(0,999999); - SP_ITEM(clip_data)->doWriteTransform(hidetrans); + if (elemref) { + elemref->setAttribute("style", "fill-rule:evenodd"); + elemref->setAttribute("class", "powerclip"); + elemref->setAttribute("id", getId().c_str()); + gchar *str = sp_svg_write_path(getClipPathvector()); + elemref->setAttribute("d", str); + g_free(str); + } else { + sp_lpe_item->removeCurrentPathEffect(false); } + } else { + sp_lpe_item->removeCurrentPathEffect(false); } - if(root) { - SPCurve * container = new SPCurve; - container->set_pathvector(clip_box); - clipcurve->append(container, false); - SP_SHAPE(elemref)->setCurve(clipcurve); - container->reset(); - delete container; - } - g_free(is_inverse_str); } -void -LPEPowerClip::updateInverse (SPItem * clip_data) { +Glib::ustring LPEPowerClip::getId() { return Glib::ustring("lpe_") + Glib::ustring(getLPEObj()->getId()); } + +void LPEPowerClip::upd() +{ SPDocument *document = getSPDoc(); - if (!document) { + if (!document || !sp_lpe_item) { return; } - if(inverse && isVisible()) { - if (SP_IS_GROUP(clip_data)) { - std::vector item_list = sp_item_group_item_list(SP_GROUP(clip_data)); - for ( std::vector::const_iterator iter=item_list.begin();iter!=item_list.end();++iter) { - SPItem *subitem = *iter; - updateInverse(subitem); - } - } else if (SP_IS_SHAPE(clip_data)) { - SPObject *elemref = document->getObjectById(Glib::ustring("lpe_") + Glib::ustring(this->getLPEObj()->getId())); - if (elemref) { - SPCurve * c = nullptr; - c = SP_SHAPE(elemref)->getCurve(); - if (c) { - Geom::PathVector c_pv = c->get_pathvector(); - //TODO: this can be not correct but no better way - bool dir_a = Geom::path_direction(c_pv[0]); - bool dir_b = Geom::path_direction(clip_box); - if (dir_a == dir_b) { - clip_box = clip_box.reversed(); - } - if(c_pv.size() > 1) { - c_pv.pop_back(); - } - c_pv.push_back(clip_box); - c->set_pathvector(c_pv); - SP_SHAPE(elemref)->setCurve(c); - c->unref(); - } - } - } + SPObject *elemref = document->getObjectById(getId().c_str()); + if (elemref && sp_lpe_item) { + gchar *str = sp_svg_write_path(getClipPathvector()); + elemref->setAttribute("d", str); + g_free(str); + elemref->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); + } else { + add(); } } -void -LPEPowerClip::removeInverse (SPItem * clip_data){ - SPDocument *document = getSPDoc(); - if (!document) { - return; - } - gchar * is_inverse_str = is_inverse.param_getSVGValue(); - if(!strcmp(is_inverse_str,"true")) { - if (SP_IS_GROUP(clip_data)) { - std::vector item_list = sp_item_group_item_list(SP_GROUP(clip_data)); - for ( std::vector::const_iterator iter=item_list.begin();iter!=item_list.end();++iter) { - SPItem *subitem = *iter; - removeInverse(subitem); - } - } else if (SP_IS_SHAPE(clip_data)) { - Geom::Affine unhidetrans = (Geom::Affine)Geom::Translate(0,-999999); - SP_ITEM(clip_data)->doWriteTransform(unhidetrans); - gchar * is_inverse_str = is_inverse.param_getSVGValue(); - if (strcmp(is_inverse_str, "false") != 0) { - is_inverse.param_setValue((Glib::ustring)"false", true); - } - } - SPObject *elemref = document->getObjectById(Glib::ustring("lpe_") + Glib::ustring(this->getLPEObj()->getId())); - if (elemref) { - elemref ->deleteObject(false); - } + +void LPEPowerClip::doBeforeEffect(SPLPEItem const *lpeitem) +{ + if (!_updating) { + upd(); } - g_free(is_inverse_str); } void LPEPowerClip::doOnRemove (SPLPEItem const* /*lpeitem*/) { - SPClipPath *clip_path = SP_ITEM(sp_lpe_item)->clip_ref->getObject(); - if(clip_path) { - if(!keep_paths) { - gchar * is_inverse_str = is_inverse.param_getSVGValue(); - std::vector clip_path_list = clip_path->childList(true); - for ( std::vector::const_iterator iter=clip_path_list.begin();iter!=clip_path_list.end();++iter) { - SPObject * clip_data = *iter; - if(!strcmp(is_inverse_str,"true")) { - removeInverse(SP_ITEM(clip_data)); + SPDocument *document = getSPDoc(); + if (!document) { + return; + } + _updating = true; + SPObject *elemref = document->getObjectById(getId().c_str()); + if (elemref) { + elemref->deleteObject(); + } + SPObject *clip_path = sp_lpe_item->clip_ref->getObject(); + if (clip_path) { + std::vector clip_path_list = clip_path->childList(true); + for (auto clip : clip_path_list) { + SPLPEItem *childitem = dynamic_cast(clip); + if (childitem) { + if (!childitem->style || childitem->style->display.set || + childitem->style->display.value == SP_CSS_DISPLAY_NONE) { + childitem->style->display.set = TRUE; + childitem->style->display.value = SP_CSS_DISPLAY_BLOCK; + childitem->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT); } } - g_free(is_inverse_str); - - } else if (flatten) { - clip_path->deleteObject(); } } } @@ -293,66 +244,46 @@ LPEPowerClip::doOnRemove (SPLPEItem const* /*lpeitem*/) Geom::PathVector LPEPowerClip::doEffect_path(Geom::PathVector const & path_in){ Geom::PathVector path_out = path_in; - SPClipPath *clip_path = SP_ITEM(sp_lpe_item)->clip_ref->getObject(); - if (!hide_clip && flatten && isVisible()) { - path_out *= sp_item_transform_repr (sp_lpe_item).inverse(); - SPDocument *document = getSPDoc(); - if (!document) { - return path_out; + if (flatten) { + Geom::PathVector c_pv = getClipPathvector(); + std::unique_ptr pig(new Geom::PathIntersectionGraph(c_pv, path_out)); + if (pig && !c_pv.empty() && !path_out.empty()) { + path_out = pig->getIntersection(); } - SPObject *elemref = document->getObjectById(Glib::ustring("lpe_") + Glib::ustring(this->getLPEObj()->getId())); - if (elemref) { - SPCurve * c = nullptr; - c = SP_SHAPE(elemref)->getCurve(); - if (c) { - Geom::PathVector c_pv = c->get_pathvector(); - c_pv *= sp_item_transform_repr (sp_lpe_item).inverse(); - std::unique_ptr pig(new Geom::PathIntersectionGraph(c_pv, path_out)); - if (pig && !c_pv.empty() && !path_in.empty()) { - path_out = pig->getIntersection(); - } - c->unref(); - } - } else { - if(clip_path) { - std::vector clip_path_list = clip_path->childList(true); - for ( std::vector::const_iterator iter=clip_path_list.begin();iter!=clip_path_list.end();++iter) { - SPObject * clip_data = *iter; - flattenClip(SP_ITEM(clip_data), path_out); - } - } - } - path_out *= sp_item_transform_repr (sp_lpe_item); } return path_out; } -void -LPEPowerClip::doOnVisibilityToggled(SPLPEItem const* lpeitem) -{ - doBeforeEffect(lpeitem); -} +void LPEPowerClip::doOnVisibilityToggled(SPLPEItem const *lpeitem) { upd(); } -void -LPEPowerClip::flattenClip(SPItem * clip_data, Geom::PathVector &path_in) +void sp_remove_powerclip(Inkscape::Selection *sel) { - if (SP_IS_GROUP(clip_data)) { - std::vector item_list = sp_item_group_item_list(SP_GROUP(clip_data)); - for ( std::vector::const_iterator iter=item_list.begin();iter!=item_list.end();++iter) { - SPItem *subitem = *iter; - flattenClip(subitem, path_in); - } - } else if (SP_IS_SHAPE(clip_data)) { - SPCurve * c = nullptr; - c = SP_SHAPE(clip_data)->getCurve(); - if (c) { - Geom::PathVector c_pv = c->get_pathvector(); - c_pv *= sp_item_transform_repr (sp_lpe_item).inverse(); - std::unique_ptr pig(new Geom::PathIntersectionGraph(c_pv, path_in)); - if (pig && !c_pv.empty() && !path_in.empty()) { - path_in = pig->getIntersection(); + if (!sel->isEmpty()) { + auto selList = sel->items(); + for (auto i = boost::rbegin(selList); i != boost::rend(selList); ++i) { + SPLPEItem *lpeitem = dynamic_cast(*i); + if (lpeitem) { + if (lpeitem->hasPathEffect() && lpeitem->pathEffectsEnabled()) { + for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); + it != lpeitem->path_effect_list->end(); ++it) { + LivePathEffectObject *lpeobj = (*it)->lpeobject; + if (!lpeobj) { + /** \todo Investigate the cause of this. + * For example, this happens when copy pasting an object with LPE applied. Probably because + * the object is pasted while the effect is not yet pasted to defs, and cannot be found. + */ + g_warning("SPLPEItem::performPathEffect - NULL lpeobj in list!"); + return; + } + Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe(); + if (lpe->getName() == "Power clip") { + lpeitem->setCurrentPathEffect(*it); + lpeitem->removeCurrentPathEffect(false); + break; + } + } + } } - c->unref(); } } } @@ -375,40 +306,7 @@ void sp_inverse_powerclip(Inkscape::Selection *sel) { } Effect::createAndApply(POWERCLIP, SP_ACTIVE_DOCUMENT, lpeitem); Effect* lpe = lpeitem->getCurrentLPE(); - lpe->getRepr()->setAttribute("is_inverse", "false"); - lpe->getRepr()->setAttribute("is_visible", "true"); lpe->getRepr()->setAttribute("inverse", "true"); - lpe->getRepr()->setAttribute("flatten", "false"); - lpe->getRepr()->setAttribute("hide_clip", "false"); - } - } - } - } -} - -void sp_remove_powerclip(Inkscape::Selection *sel) { - if (!sel->isEmpty()) { - auto selList = sel->items(); - for(auto i = boost::rbegin(selList); i != boost::rend(selList); ++i) { - SPLPEItem* lpeitem = dynamic_cast(*i); - if (lpeitem) { - if (lpeitem->hasPathEffect() && lpeitem->pathEffectsEnabled()) { - for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it) - { - LivePathEffectObject *lpeobj = (*it)->lpeobject; - if (!lpeobj) { - /** \todo Investigate the cause of this. - * For example, this happens when copy pasting an object with LPE applied. Probably because the object is pasted while the effect is not yet pasted to defs, and cannot be found. - */ - g_warning("SPLPEItem::performPathEffect - NULL lpeobj in list!"); - return; - } - Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe(); - if (lpe->getName() == "powerclip") { - lpe->doOnRemove(lpeitem); - break; - } - } } } } diff --git a/src/live_effects/lpe-powerclip.h b/src/live_effects/lpe-powerclip.h index d1b4098d8..fab59ed1e 100644 --- a/src/live_effects/lpe-powerclip.h +++ b/src/live_effects/lpe-powerclip.h @@ -10,7 +10,6 @@ #include "live_effects/effect.h" #include "live_effects/parameter/message.h" -#include "live_effects/parameter/hidden.h" namespace Inkscape { namespace LivePathEffect { @@ -19,26 +18,23 @@ class LPEPowerClip : public Effect { public: LPEPowerClip(LivePathEffectObject *lpeobject); ~LPEPowerClip() override; - void doOnApply (SPLPEItem const * lpeitem) override; void doBeforeEffect (SPLPEItem const* lpeitem) override; Geom::PathVector doEffect_path (Geom::PathVector const & path_in) override; void doOnRemove (SPLPEItem const* /*lpeitem*/) override; void doOnVisibilityToggled(SPLPEItem const* lpeitem) override; - void doAfterEffect (SPLPEItem const* lpeitem) override; - void addInverse (SPItem * clip_data, SPCurve * clipcurve, Geom::Affine affine, bool root); - void updateInverse (SPItem * clip_data); - void removeInverse (SPItem * clip_data); - void flattenClip(SPItem * clip_data, Geom::PathVector &path_in); -private: - HiddenParam is_inverse; - HiddenParam uri; + Glib::ustring getId(); + void add(); + void upd(); + void del(); + Geom::PathVector getClipPathvector(); + + private: BoolParam inverse; BoolParam flatten; BoolParam hide_clip; MessageParam message; - Geom::Path clip_box; - Geom::Affine base; - Geom::Affine lastapplied; + bool _updating; + bool _legacy; }; void sp_remove_powerclip(Inkscape::Selection *sel); diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp index d12d8e465..884bf6be4 100644 --- a/src/object/sp-item.cpp +++ b/src/object/sp-item.cpp @@ -791,7 +791,7 @@ Geom::OptRect SPItem::geometricBounds(Geom::Affine const &transform) const return bbox; } -Geom::OptRect SPItem::visualBounds(Geom::Affine const &transform) const +Geom::OptRect SPItem::visualBounds(Geom::Affine const &transform, bool wfilter, bool wclip, bool wmask) const { using Geom::X; using Geom::Y; @@ -800,7 +800,7 @@ Geom::OptRect SPItem::visualBounds(Geom::Affine const &transform) const SPFilter *filter = (style && style->filter.href) ? dynamic_cast(style->getFilter()) : nullptr; - if ( filter ) { + if (filter && wfilter) { // call the subclass method // CPPIFY //bbox = this->bbox(Geom::identity(), SPItem::VISUAL_BBOX); @@ -851,13 +851,13 @@ Geom::OptRect SPItem::visualBounds(Geom::Affine const &transform) const //bbox = this->bbox(transform, SPItem::VISUAL_BBOX); bbox = const_cast(this)->bbox(transform, SPItem::VISUAL_BBOX); } - if (clip_ref->getObject()) { + if (clip_ref->getObject() && wclip) { SPItem *ownerItem = dynamic_cast(clip_ref->getOwner()); g_assert(ownerItem != nullptr); ownerItem->bbox_valid = FALSE; // LP Bug 1349018 bbox.intersectWith(clip_ref->getObject()->geometricBounds(transform)); } - if (mask_ref->getObject()) { + if (mask_ref->getObject() && wmask) { bbox_valid = false; // LP Bug 1349018 bbox.intersectWith(mask_ref->getObject()->visualBounds(transform)); } diff --git a/src/object/sp-item.h b/src/object/sp-item.h index 37faf999b..180142ec1 100644 --- a/src/object/sp-item.h +++ b/src/object/sp-item.h @@ -246,8 +246,12 @@ public: * Get item's visual bounding box in this item's coordinate system. * * The visual bounding box includes the stroke and the filter region. + * @param wfilter use filter expand in bbox calculation + * @param wclip use clip data in bbox calculation + * @param wmask use mask data in bbox calculation */ - Geom::OptRect visualBounds(Geom::Affine const &transform = Geom::identity()) const; + Geom::OptRect visualBounds(Geom::Affine const &transform = Geom::identity(), bool wfilter = true, bool wclip = true, + bool wmask = true) const; Geom::OptRect bounds(BBoxType type, Geom::Affine const &transform = Geom::identity()) const; diff --git a/src/verbs.cpp b/src/verbs.cpp index 2c10cb6cf..49ee61aab 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1551,7 +1551,6 @@ void ObjectVerb::perform( SPAction *action, void *data) center = *sel->center(); else center = bbox->midpoint(); - switch (reinterpret_cast(data)) { case SP_VERB_OBJECT_ROTATE_90_CW: sel->rotate90(false); @@ -1586,13 +1585,17 @@ void ObjectVerb::perform( SPAction *action, void *data) break; case SP_VERB_OBJECT_SET_INVERSE_MASK: sel->setMask(false, false); + Inkscape::DocumentUndo::setUndoSensitive(dt->getDocument(), false); Inkscape::LivePathEffect::sp_inverse_powermask(sp_action_get_selection(action)); + Inkscape::DocumentUndo::setUndoSensitive(dt->getDocument(), true); break; case SP_VERB_OBJECT_EDIT_MASK: sel->editMask(false); break; case SP_VERB_OBJECT_UNSET_MASK: + Inkscape::DocumentUndo::setUndoSensitive(dt->getDocument(), false); Inkscape::LivePathEffect::sp_remove_powermask(sp_action_get_selection(action)); + Inkscape::DocumentUndo::setUndoSensitive(dt->getDocument(), true); sel->unsetMask(false); break; case SP_VERB_OBJECT_SET_CLIPPATH: @@ -1600,7 +1603,9 @@ void ObjectVerb::perform( SPAction *action, void *data) break; case SP_VERB_OBJECT_SET_INVERSE_CLIPPATH: sel->setMask(true, false); + Inkscape::DocumentUndo::setUndoSensitive(dt->getDocument(), false); Inkscape::LivePathEffect::sp_inverse_powerclip(sp_action_get_selection(action)); + Inkscape::DocumentUndo::setUndoSensitive(dt->getDocument(), true); break; case SP_VERB_OBJECT_CREATE_CLIP_GROUP: sel->setClipGroup(); @@ -1609,7 +1614,9 @@ void ObjectVerb::perform( SPAction *action, void *data) sel->editMask(true); break; case SP_VERB_OBJECT_UNSET_CLIPPATH: + Inkscape::DocumentUndo::setUndoSensitive(dt->getDocument(), false); Inkscape::LivePathEffect::sp_remove_powerclip(sp_action_get_selection(action)); + Inkscape::DocumentUndo::setUndoSensitive(dt->getDocument(), true); sel->unsetMask(true); break; default: diff --git a/testfiles/rendering_tests/CMakeLists.txt b/testfiles/rendering_tests/CMakeLists.txt index afe17ba96..bc983ccfb 100644 --- a/testfiles/rendering_tests/CMakeLists.txt +++ b/testfiles/rendering_tests/CMakeLists.txt @@ -14,6 +14,8 @@ set(RENDERING_TESTS selector-important-002 selector-important-003 test-dont-crash + test-lpe-0.92 + test-lpe-1.0 ) diff --git a/testfiles/rendering_tests/README b/testfiles/rendering_tests/README index 92cf95ef6..4c54b039e 100644 --- a/testfiles/rendering_tests/README +++ b/testfiles/rendering_tests/README @@ -2,8 +2,12 @@ HOWTO # Add a rendering test: - create the svg file - - inkscape .svg -d 96 -e expected_rendering/.png - - inkscape .svg -d 384 -e expected_rendering/-large.png + - 0.92: + - inkscape .svg -d 96 -e expected_rendering/.png + - inkscape .svg -d 384 -e expected_rendering/-large.png + - 1.0: + - inkscape -d 96 --export-file=expected_rendering/.png .svg + - inkscape -d 384 --export-file=expected_rendering/-large.png .svg - add the test in CMakeLists.txt - use stable if possible to generate the reference png files - git add .svg expected_rendering/-large.png expected_rendering/.png diff --git a/testfiles/rendering_tests/expected_rendering/test-lpe-0.92-large.png b/testfiles/rendering_tests/expected_rendering/test-lpe-0.92-large.png new file mode 100644 index 000000000..65a201eae Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-lpe-0.92-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-lpe-0.92.png b/testfiles/rendering_tests/expected_rendering/test-lpe-0.92.png new file mode 100644 index 000000000..b255e7368 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-lpe-0.92.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-lpe-1.0-large.png b/testfiles/rendering_tests/expected_rendering/test-lpe-1.0-large.png new file mode 100644 index 000000000..78abd53ec Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-lpe-1.0-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-lpe-1.0.png b/testfiles/rendering_tests/expected_rendering/test-lpe-1.0.png new file mode 100644 index 000000000..d88b60cb8 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-lpe-1.0.png differ diff --git a/testfiles/rendering_tests/test-lpe-0.92.svg b/testfiles/rendering_tests/test-lpe-0.92.svg new file mode 100644 index 000000000..b17c9ae13 --- /dev/null +++ b/testfiles/rendering_tests/test-lpe-0.92.svg @@ -0,0 +1,4034 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + no lpe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/rendering_tests/test-lpe-1.0.svg b/testfiles/rendering_tests/test-lpe-1.0.svg new file mode 100644 index 000000000..79385b960 --- /dev/null +++ b/testfiles/rendering_tests/test-lpe-1.0.svg @@ -0,0 +1,4247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + no lpe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3