summaryrefslogtreecommitdiffstats
path: root/src/filter-chemistry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/filter-chemistry.cpp')
-rw-r--r--src/filter-chemistry.cpp564
1 files changed, 564 insertions, 0 deletions
diff --git a/src/filter-chemistry.cpp b/src/filter-chemistry.cpp
new file mode 100644
index 0000000..e5dc50d
--- /dev/null
+++ b/src/filter-chemistry.cpp
@@ -0,0 +1,564 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Various utility methods for filters
+ *
+ * Authors:
+ * Hugo Rodrigues
+ * bulia byak
+ * Niko Kiirala
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 2006-2008 authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "filter-chemistry.h"
+
+#include <cstring>
+#include <glibmm.h>
+
+#include "desktop-style.h"
+#include "document.h"
+#include "filter-enums.h"
+#include "style.h"
+
+#include "object/sp-defs.h"
+#include "object/sp-item.h"
+
+#include "object/filters/blend.h"
+#include "object/filters/gaussian-blur.h"
+
+/**
+ * Count how many times the filter is used by the styles of o and its
+ * descendants
+ */
+static guint count_filter_hrefs(SPObject *o, SPFilter *filter)
+{
+ if (!o)
+ return 1;
+
+ guint i = 0;
+
+ SPStyle *style = o->style;
+ if (style
+ && style->filter.set
+ && style->getFilter() == filter)
+ {
+ i ++;
+ }
+
+ for (auto& child: o->children) {
+ i += count_filter_hrefs(&child, filter);
+ }
+
+ return i;
+}
+
+SPFilter *new_filter(SPDocument *document)
+{
+ g_return_val_if_fail(document != nullptr, NULL);
+
+ SPDefs *defs = document->getDefs();
+
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+
+ // create a new filter
+ Inkscape::XML::Node *repr;
+ repr = xml_doc->createElement("svg:filter");
+
+ // Inkscape now supports both sRGB and linear color-interpolation-filters.
+ // But, for the moment, keep sRGB as default value for new filters
+ // (historically set to sRGB and doesn't require conversion between
+ // filter cairo surfaces and other types of cairo surfaces).
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "color-interpolation-filters", "sRGB");
+ sp_repr_css_change(repr, css, "style");
+ sp_repr_css_attr_unref(css);
+
+ // Append the new filter node to defs
+ defs->appendChild(repr);
+ Inkscape::GC::release(repr);
+
+ // get corresponding object
+ SPFilter *f = SP_FILTER( document->getObjectByRepr(repr) );
+
+
+ g_assert(f != nullptr);
+ g_assert(SP_IS_FILTER(f));
+
+ return f;
+}
+
+SPFilterPrimitive *
+filter_add_primitive(SPFilter *filter, const Inkscape::Filters::FilterPrimitiveType type)
+{
+ Inkscape::XML::Document *xml_doc = filter->document->getReprDoc();
+
+ //create filter primitive node
+ Inkscape::XML::Node *repr;
+ repr = xml_doc->createElement(FPConverter.get_key(type).c_str());
+
+ // set default values
+ switch(type) {
+ case Inkscape::Filters::NR_FILTER_BLEND:
+ repr->setAttribute("mode", "normal");
+ break;
+ case Inkscape::Filters::NR_FILTER_COLORMATRIX:
+ break;
+ case Inkscape::Filters::NR_FILTER_COMPONENTTRANSFER:
+ break;
+ case Inkscape::Filters::NR_FILTER_COMPOSITE:
+ break;
+ case Inkscape::Filters::NR_FILTER_CONVOLVEMATRIX:
+ repr->setAttribute("order", "3 3");
+ repr->setAttribute("kernelMatrix", "0 0 0 0 0 0 0 0 0");
+ break;
+ case Inkscape::Filters::NR_FILTER_DIFFUSELIGHTING:
+ break;
+ case Inkscape::Filters::NR_FILTER_DISPLACEMENTMAP:
+ break;
+ case Inkscape::Filters::NR_FILTER_FLOOD:
+ break;
+ case Inkscape::Filters::NR_FILTER_GAUSSIANBLUR:
+ repr->setAttribute("stdDeviation", "1");
+ break;
+ case Inkscape::Filters::NR_FILTER_IMAGE:
+ break;
+ case Inkscape::Filters::NR_FILTER_MERGE:
+ break;
+ case Inkscape::Filters::NR_FILTER_MORPHOLOGY:
+ break;
+ case Inkscape::Filters::NR_FILTER_OFFSET:
+ repr->setAttribute("dx", "0");
+ repr->setAttribute("dy", "0");
+ break;
+ case Inkscape::Filters::NR_FILTER_SPECULARLIGHTING:
+ break;
+ case Inkscape::Filters::NR_FILTER_TILE:
+ break;
+ case Inkscape::Filters::NR_FILTER_TURBULENCE:
+ break;
+ default:
+ break;
+ }
+
+ //set primitive as child of filter node
+ // XML tree being used directly while/where it shouldn't be...
+ filter->appendChild(repr);
+ Inkscape::GC::release(repr);
+
+ // get corresponding object
+ SPFilterPrimitive *prim = SP_FILTER_PRIMITIVE( filter->document->getObjectByRepr(repr) );
+
+ g_assert(prim != nullptr);
+ g_assert(SP_IS_FILTER_PRIMITIVE(prim));
+
+ return prim;
+}
+
+/**
+ * Creates a filter with blur primitive of specified radius for an item with the given matrix expansion, width and height
+ */
+SPFilter *
+new_filter_gaussian_blur (SPDocument *document, gdouble radius, double expansion)
+{
+ g_return_val_if_fail(document != nullptr, NULL);
+
+ SPDefs *defs = document->getDefs();
+
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+
+ // create a new filter
+ Inkscape::XML::Node *repr;
+ repr = xml_doc->createElement("svg:filter");
+ //repr->setAttribute("inkscape:collect", "always");
+
+
+ /* Inkscape now supports both sRGB and linear color-interpolation-filters.
+ * But, for the moment, keep sRGB as default value for new filters.
+ * historically set to sRGB and doesn't require conversion between
+ * filter cairo surfaces and other types of cairo surfaces. lp:1127103 */
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "color-interpolation-filters", "sRGB");
+ sp_repr_css_change(repr, css, "style");
+ sp_repr_css_attr_unref(css);
+
+ //create feGaussianBlur node
+ Inkscape::XML::Node *b_repr;
+ b_repr = xml_doc->createElement("svg:feGaussianBlur");
+ //b_repr->setAttribute("inkscape:collect", "always");
+
+ double stdDeviation = radius;
+ if (expansion != 0)
+ stdDeviation /= expansion;
+
+ //set stdDeviation attribute
+ b_repr->setAttributeSvgDouble("stdDeviation", stdDeviation);
+
+ //set feGaussianBlur as child of filter node
+ repr->appendChild(b_repr);
+ Inkscape::GC::release(b_repr);
+
+ // Append the new filter node to defs
+ defs->appendChild(repr);
+ Inkscape::GC::release(repr);
+
+ // get corresponding object
+ SPFilter *f = SP_FILTER( document->getObjectByRepr(repr) );
+ SPGaussianBlur *b = SP_GAUSSIANBLUR( document->getObjectByRepr(b_repr) );
+
+ g_assert(f != nullptr);
+ g_assert(SP_IS_FILTER(f));
+ g_assert(b != nullptr);
+ g_assert(SP_IS_GAUSSIANBLUR(b));
+
+ return f;
+}
+
+
+/**
+ * Creates a simple filter with a blend primitive and a blur primitive of specified radius for
+ * an item with the given matrix expansion, width and height
+ */
+static SPFilter *
+new_filter_blend_gaussian_blur (SPDocument *document, const char *blendmode, gdouble radius, double expansion)
+{
+ g_return_val_if_fail(document != nullptr, NULL);
+
+ SPDefs *defs = document->getDefs();
+
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+
+ // create a new filter
+ Inkscape::XML::Node *repr;
+ repr = xml_doc->createElement("svg:filter");
+ repr->setAttribute("inkscape:collect", "always");
+
+ /* Inkscape now supports both sRGB and linear color-interpolation-filters.
+ * But, for the moment, keep sRGB as default value for new filters.
+ * historically set to sRGB and doesn't require conversion between
+ * filter cairo surfaces and other types of cairo surfaces. lp:1127103 */
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "color-interpolation-filters", "sRGB");
+ sp_repr_css_change(repr, css, "style");
+ sp_repr_css_attr_unref(css);
+
+ // Append the new filter node to defs
+ defs->appendChild(repr);
+ Inkscape::GC::release(repr);
+
+ // get corresponding object
+ SPFilter *f = SP_FILTER( document->getObjectByRepr(repr) );
+ // Gaussian blur primitive
+ if(radius != 0) {
+ //create feGaussianBlur node
+ Inkscape::XML::Node *b_repr;
+ b_repr = xml_doc->createElement("svg:feGaussianBlur");
+ b_repr->setAttribute("inkscape:collect", "always");
+
+ double stdDeviation = radius;
+ if (expansion != 0)
+ stdDeviation /= expansion;
+
+ //set stdDeviation attribute
+ b_repr->setAttributeSvgDouble("stdDeviation", stdDeviation);
+
+ //set feGaussianBlur as child of filter node
+ repr->appendChild(b_repr);
+ Inkscape::GC::release(b_repr);
+
+ SPGaussianBlur *b = SP_GAUSSIANBLUR( document->getObjectByRepr(b_repr) );
+ g_assert(b != nullptr);
+ g_assert(SP_IS_GAUSSIANBLUR(b));
+ }
+ // Blend primitive
+ if(strcmp(blendmode, "normal")) {
+ Inkscape::XML::Node *b_repr;
+ b_repr = xml_doc->createElement("svg:feBlend");
+ b_repr->setAttribute("inkscape:collect", "always");
+ b_repr->setAttribute("mode", blendmode);
+ b_repr->setAttribute("in2", "BackgroundImage");
+
+ // set feBlend as child of filter node
+ repr->appendChild(b_repr);
+ Inkscape::GC::release(b_repr);
+
+ // Enable background image buffer for document
+ Inkscape::XML::Node *root = b_repr->root();
+ if (!root->attribute("enable-background")) {
+ root->setAttribute("enable-background", "new");
+ }
+
+ SPFeBlend *b = SP_FEBLEND(document->getObjectByRepr(b_repr));
+ g_assert(b != nullptr);
+ g_assert(SP_IS_FEBLEND(b));
+ }
+
+ g_assert(f != nullptr);
+ g_assert(SP_IS_FILTER(f));
+
+ return f;
+}
+
+/**
+ * Creates a simple filter for the given item with blend and blur primitives, using the
+ * specified mode and radius, respectively
+ */
+SPFilter *
+new_filter_simple_from_item (SPDocument *document, SPItem *item, const char *mode, gdouble radius)
+{
+ return new_filter_blend_gaussian_blur(document, mode, radius, item->i2dt_affine().descrim());
+}
+
+/**
+ * Modifies the gaussian blur applied to the item.
+ * If no filters are applied to given item, creates a new blur filter.
+ * If a filter is applied and it contains a blur, modify that blur.
+ * If the filter doesn't contain blur, a blur is added to the filter.
+ * Should there be more references to modified filter, that filter is
+ * duplicated, so that other elements referring that filter are not modified.
+ */
+/* TODO: this should be made more generic, not just for blurs */
+SPFilter *modify_filter_gaussian_blur_from_item(SPDocument *document, SPItem *item,
+ gdouble radius)
+{
+ if (!item->style || !item->style->filter.set) {
+ return new_filter_simple_from_item(document, item, "normal", radius);
+ }
+
+ SPFilter *filter = item->style->getFilter();
+ if (!filter) {
+ // We reach here when filter.set is true, but the href is not found in the document
+ return new_filter_simple_from_item(document, item, "normal", radius);
+ }
+
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+
+ // If there are more users for this filter, duplicate it
+ if (filter->hrefcount > count_filter_hrefs(item, filter)) {
+ Inkscape::XML::Node *repr = item->style->getFilter()->getRepr()->duplicate(xml_doc);
+ SPDefs *defs = document->getDefs();
+ defs->appendChild(repr);
+
+ filter = SP_FILTER( document->getObjectByRepr(repr) );
+ Inkscape::GC::release(repr);
+ }
+
+ // Determine the required standard deviation value
+ Geom::Affine i2d (item->i2dt_affine ());
+ double expansion = i2d.descrim();
+ double stdDeviation = radius;
+ if (expansion != 0)
+ stdDeviation /= expansion;
+
+ // Set the filter effects area
+ SPFilter *f = item->style->getFilter();
+
+ Inkscape::XML::Node *repr = f->getRepr();
+ // Search for gaussian blur primitives. If found, set the stdDeviation
+ // of the first one and return.
+ Inkscape::XML::Node *primitive = repr->firstChild();
+ while (primitive) {
+ if (strcmp("svg:feGaussianBlur", primitive->name()) == 0) {
+ primitive->setAttributeSvgDouble("stdDeviation", stdDeviation);
+ return filter;
+ }
+ primitive = primitive->next();
+ }
+
+ // If there were no gaussian blur primitives, create a new one
+
+ //create feGaussianBlur node
+ Inkscape::XML::Node *b_repr;
+ b_repr = xml_doc->createElement("svg:feGaussianBlur");
+ //b_repr->setAttribute("inkscape:collect", "always");
+
+ //set stdDeviation attribute
+ b_repr->setAttributeSvgDouble("stdDeviation", stdDeviation);
+
+ //set feGaussianBlur as child of filter node
+ filter->getRepr()->appendChild(b_repr);
+ Inkscape::GC::release(b_repr);
+
+ return filter;
+}
+
+void remove_filter (SPObject *item, bool recursive)
+{
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_unset_property(css, "filter");
+ if (recursive) {
+ sp_repr_css_change_recursive(item->getRepr(), css, "style");
+ } else {
+ sp_repr_css_change(item->getRepr(), css, "style");
+ }
+ sp_repr_css_attr_unref(css);
+}
+
+void remove_hidder_filter (SPObject *item)
+{
+ SPFilter *filt = item->style->getFilter();
+ if (filt && filt->getId()) {
+ Glib::ustring filter = filt->getId();
+ if (!filter.rfind("selectable_hidder_filter", 0)) {
+ remove_filter(item, false);
+ }
+ }
+}
+
+bool has_hidder_filter(SPObject *item)
+{
+ SPFilter *filt = item->style->getFilter();
+ if (filt && filt->getId()) {
+ Glib::ustring filter = filt->getId();
+ if (!filter.rfind("selectable_hidder_filter", 0)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Removes the first feGaussianBlur from the filter attached to given item.
+ * Should this leave us with an empty filter, remove that filter.
+ */
+/* TODO: the removed filter primitive may had had a named result image, so
+ * after removing, the filter may be in erroneous state, this situation should
+ * be handled gracefully */
+void remove_filter_gaussian_blur (SPObject *item)
+{
+ if (item->style && item->style->filter.set && item->style->getFilter()) {
+ // Search for the first blur primitive and remove it. (if found)
+ Inkscape::XML::Node *repr = item->style->getFilter()->getRepr();
+ Inkscape::XML::Node *primitive = repr->firstChild();
+ while (primitive) {
+ if (strcmp("svg:feGaussianBlur", primitive->name()) == 0) {
+ sp_repr_unparent(primitive);
+ break;
+ }
+ primitive = primitive->next();
+ }
+
+ // If there are no more primitives left in this filter, discard it.
+ if (repr->childCount() == 0) {
+ remove_filter(item, false);
+ }
+ }
+}
+
+/**
+ * Removes blend primitive from the filter attached to given item.
+ * Get if the filter have a < 1.0 blending filter and if it remove it
+ * @params: the item to remove filtered blend
+ */
+/* TODO: the removed filter primitive may had had a named result image, so
+ * after removing, the filter may be in erroneous state, this situation should
+ * be handled gracefully */
+void remove_filter_legacy_blend(SPObject *item)
+{
+ if (!item) {
+ return;
+ }
+ if (item->style && item->style->filter.set && item->style->getFilter()) {
+ // Search for the first blur primitive and remove it. (if found)
+ size_t blurcount = 0;
+ size_t blendcount = 0;
+ size_t total = 0;
+ // determine whether filter is simple (blend and/or blur) or complex
+ SPFeBlend *blend = nullptr;
+ for (auto &primitive_obj:item->style->getFilter()->children) {
+ SPFilterPrimitive *primitive = dynamic_cast<SPFilterPrimitive *>(&primitive_obj);
+ if (primitive) {
+ if (dynamic_cast<SPFeBlend *>(primitive)) {
+ blend = dynamic_cast<SPFeBlend *>(primitive);
+ ++blendcount;
+ }
+ SPGaussianBlur *spgausian = dynamic_cast<SPGaussianBlur *>(primitive);
+ if (spgausian) {
+ ++blurcount;
+ }
+ ++total;
+ }
+ }
+ if (blend && total == 2 && blurcount == 1) {
+ blend->deleteObject(true);
+ } else if (total == 1 && blurcount != 1) {
+ remove_filter(item, false);
+ }
+ }
+}
+
+/**
+ * Get if the filter have a < 1.0 blending filter
+ * @params: the item to get filtered blend
+ */
+SPBlendMode filter_get_legacy_blend(SPObject *item)
+{
+ auto blend = SP_CSS_BLEND_NORMAL;
+ if (!item) {
+ return blend;
+ }
+ if (item->style && item->style->filter.set && item->style->getFilter()) {
+ // Search for the first blur primitive and remove it. (if found)
+ size_t blurcount = 0;
+ size_t blendcount = 0;
+ size_t total = 0;
+ // determine whether filter is simple (blend and/or blur) or complex
+ for (auto &primitive_obj:item->style->getFilter()->children) {
+ SPFilterPrimitive *primitive = dynamic_cast<SPFilterPrimitive *>(&primitive_obj);
+ if (primitive) {
+ SPFeBlend *spblend = dynamic_cast<SPFeBlend *>(primitive);
+ if (spblend) {
+ ++blendcount;
+ blend = spblend->blend_mode;
+ }
+ SPGaussianBlur *spgausian = dynamic_cast<SPGaussianBlur *>(primitive);
+ if (spgausian) {
+ ++blurcount;
+ }
+ ++total;
+ }
+ }
+ if (!((blend && total == 2 && blurcount == 1) || total == 1)) {
+ blend = SP_CSS_BLEND_NORMAL;
+ }
+ }
+ return blend;
+}
+
+bool filter_is_single_gaussian_blur(SPFilter *filter)
+{
+ return (filter->children.size() == 1 &&
+ SP_IS_GAUSSIANBLUR(&filter->children.front()));
+}
+
+double get_single_gaussian_blur_radius(SPFilter *filter)
+{
+ if (filter->children.size() == 1 &&
+ SP_IS_GAUSSIANBLUR(&filter->children.front())) {
+
+ SPGaussianBlur *gb = SP_GAUSSIANBLUR(filter->firstChild());
+ double x = gb->stdDeviation.getNumber();
+ double y = gb->stdDeviation.getOptNumber();
+ if (x > 0 && y > 0) {
+ return MAX(x, y);
+ }
+ return x;
+ }
+ return 0.0;
+}
+
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :