Code

Modified filter rendering area handling to better accommodate upcoming feOffset
[inkscape.git] / src / sp-gradient.cpp
index c2b0b32a3cb881169f6562a13d5ef59e649f38ea..d5a733b4ff4c452b796e2508c928116ec2d533b0 100644 (file)
 #include <libnr/nr-matrix-translate-ops.h>
 #include "libnr/nr-scale-translate-ops.h"
 
+#include <sigc++/functors/ptr_fun.h>
+#include <sigc++/adaptors/bind.h>
 
 #include "display/nr-gradient-gpl.h"
 #include "svg/svg.h"
+#include "svg/svg-color.h"
 #include "svg/css-ostringstream.h"
 #include "attributes.h"
 #include "document-private.h"
@@ -181,7 +184,7 @@ sp_stop_set(SPObject *object, unsigned key, gchar const *value)
         }
         case SP_ATTR_OFFSET: {
             stop->offset = sp_svg_read_percentage(value, 0.0);
-            object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+            object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
             break;
         }
         default: {
@@ -201,9 +204,16 @@ sp_stop_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
     SPStop *stop = SP_STOP(object);
 
     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
-        repr = sp_repr_new("svg:stop");
+        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
+        repr = xml_doc->createElement("svg:stop");
     }
 
+    if (((SPObjectClass *) stop_parent_class)->write)
+        (* ((SPObjectClass *) stop_parent_class)->write)(object, repr, flags);
+
+    // Since we do a hackish style setting here (because SPStyle does not support stop-color and
+    // stop-opacity), we must do it AFTER calling the parent write method; otherwise
+    // sp_object_write would clear our style= attribute (bug 1695287)
 
     Inkscape::CSSOStringStream os;
     os << "stop-color:";
@@ -222,9 +232,6 @@ sp_stop_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
     /* strictly speaking, offset an SVG <number> rather than a CSS one, but exponents make no sense
      * for offset proportions. */
 
-    if (((SPObjectClass *) stop_parent_class)->write)
-        (* ((SPObjectClass *) stop_parent_class)->write)(object, repr, flags);
-
     return repr;
 }
 
@@ -301,6 +308,9 @@ static void sp_gradient_rebuild_vector(SPGradient *gr);
 
 static void gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gradient);
 
+SPGradientSpread sp_gradient_get_spread(SPGradient *gradient);
+SPGradientUnits sp_gradient_get_units(SPGradient *gradient);
+
 static SPPaintServerClass *gradient_parent_class;
 
 /**
@@ -376,6 +386,8 @@ sp_gradient_init(SPGradient *gr)
     gr->vector.stops.clear();
 
     gr->color = NULL;
+
+    new (&gr->modified_connection) sigc::connection();
 }
 
 /**
@@ -424,9 +436,7 @@ sp_gradient_release(SPObject *object)
     }
 
     if (gradient->ref) {
-        if (gradient->ref->getObject()) {
-            sp_signal_disconnect_by_data(gradient->ref->getObject(), gradient);
-        }
+        gradient->modified_connection.disconnect();
         gradient->ref->detach();
         delete gradient->ref;
         gradient->ref = NULL;
@@ -437,6 +447,8 @@ sp_gradient_release(SPObject *object)
         gradient->color = NULL;
     }
 
+    gradient->modified_connection.~connection();
+
     if (((SPObjectClass *) gradient_parent_class)->release)
         ((SPObjectClass *) gradient_parent_class)->release(object);
 }
@@ -517,13 +529,23 @@ static void
 gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gr)
 {
     if (old_ref) {
-        sp_signal_disconnect_by_data(old_ref, gr);
+        gr->modified_connection.disconnect();
     }
     if ( SP_IS_GRADIENT(ref)
          && ref != gr )
     {
-        g_signal_connect(G_OBJECT(ref), "modified", G_CALLBACK(gradient_ref_modified), gr);
+        gr->modified_connection = ref->connectModified(sigc::bind<2>(sigc::ptr_fun(&gradient_ref_modified), gr));
     }
+
+    // Per SVG, all unset attributes must be inherited from linked gradient. 
+    // So, as we're now (re)linked, we assign linkee's values to this gradient if they are not yet set -
+    // but without setting the _set flags. 
+    // FIXME: do the same for gradientTransform too
+    if (!gr->units_set)
+        gr->units = sp_gradient_get_units (gr);
+    if (!gr->spread_set)
+        gr->spread = sp_gradient_get_spread (gr);
+
     /// \todo Fixme: what should the flags (second) argument be? */
     gradient_ref_modified(ref, 0, gr);
 }
@@ -588,6 +610,10 @@ sp_gradient_modified(SPObject *object, guint flags)
         sp_gradient_invalidate_vector(gr);
     }
 
+    if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
+        sp_gradient_ensure_colors(gr);
+    }
+    
     if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
     flags &= SP_OBJECT_MODIFIED_CASCADE;
 
@@ -651,12 +677,9 @@ sp_gradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
     }
 
     if ((flags & SP_OBJECT_WRITE_ALL) || gr->gradientTransform_set) {
-        gchar c[256];
-        if (sp_svg_transform_write(c, 256, gr->gradientTransform)) {
-            repr->setAttribute("gradientTransform", c);
-        } else {
-            repr->setAttribute("gradientTransform", NULL);
-        }
+        gchar *c=sp_svg_transform_write(gr->gradientTransform);
+        repr->setAttribute("gradientTransform", c);
+        g_free(c);
     }
 
     if ((flags & SP_OBJECT_WRITE_ALL) || gr->spread_set) {
@@ -781,6 +804,15 @@ has_spread_set(SPGradient const *gr)
     return gr->spread_set;
 }
 
+/**
+ * True if gradient has units set.
+ */
+static bool
+has_units_set(SPGradient const *gr)
+{
+    return gr->units_set;
+}
+
 
 /**
  * Returns private vector of given gradient (the gradient at the end of the href chain which has
@@ -817,6 +849,23 @@ sp_gradient_get_spread(SPGradient *gradient)
              : SP_GRADIENT_SPREAD_PAD ); // pad is the default
 }
 
+/**
+ * Returns the effective units of given gradient (climbing up the refs chain if needed).
+ *
+ * \pre SP_IS_GRADIENT(gradient).
+ */
+SPGradientUnits
+sp_gradient_get_units(SPGradient *gradient)
+{
+    g_return_val_if_fail(SP_IS_GRADIENT(gradient), SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX);
+
+    SPGradient const *src = chase_hrefs(gradient, has_units_set);
+    return ( src
+             ? src->units
+             : SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ); // bbox is the default
+}
+
+
 /**
  * Clears the gradient's svg:stop children from its repr.
  */
@@ -853,6 +902,7 @@ sp_gradient_repr_write_vector(SPGradient *gr)
     g_return_if_fail(gr != NULL);
     g_return_if_fail(SP_IS_GRADIENT(gr));
 
+    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(gr));
     Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr);
 
     /* We have to be careful, as vector may be our own, so construct repr list at first */
@@ -860,7 +910,7 @@ sp_gradient_repr_write_vector(SPGradient *gr)
 
     for (guint i = 0; i < gr->vector.stops.size(); i++) {
         Inkscape::CSSOStringStream os;
-        Inkscape::XML::Node *child = sp_repr_new("svg:stop");
+        Inkscape::XML::Node *child = xml_doc->createElement("svg:stop");
         sp_repr_set_css_double(child, "offset", gr->vector.stops[i].offset);
         /* strictly speaking, offset an SVG <number> rather than a CSS one, but exponents make no
          * sense for offset proportions. */
@@ -1229,7 +1279,7 @@ sp_gradient_set_gs2d_matrix(SPGradient *gr, NR::Matrix const &ctm,
                             NR::Rect const &bbox, NR::Matrix const &gs2d)
 {
     gr->gradientTransform = gs2d / ctm;
-    if ( gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) {
+    if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) {
         gr->gradientTransform = ( gr->gradientTransform
                                   / NR::translate(bbox.min())
                                   / NR::scale(bbox.dimensions()) );
@@ -1382,7 +1432,8 @@ sp_lineargradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags
     SPLinearGradient *lg = SP_LINEARGRADIENT(object);
 
     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
-        repr = sp_repr_new("svg:linearGradient");
+        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
+        repr = xml_doc->createElement("svg:linearGradient");
     }
 
     if ((flags & SP_OBJECT_WRITE_ALL) || lg->x1._set)
@@ -1683,7 +1734,8 @@ sp_radialgradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags
     SPRadialGradient *rg = SP_RADIALGRADIENT(object);
 
     if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
-        repr = sp_repr_new("svg:radialGradient");
+        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
+        repr = xml_doc->createElement("svg:radialGradient");
     }
 
     if ((flags & SP_OBJECT_WRITE_ALL) || rg->cx._set) sp_repr_set_svg_double(repr, "cx", rg->cx.computed);