Code

Filter effects dialog:
[inkscape.git] / src / sp-gradient.cpp
index db2360225c6c4b033e0115b7bd46602bfdaeb09e..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"
@@ -44,7 +47,7 @@
 #define SP_MACROS_SILENT
 #include "macros.h"
 
-/// Has to be power of 2 
+/// Has to be power of 2
 #define NCOLORS NR_GRADIENT_VECTOR_LENGTH
 
 static void sp_stop_class_init(SPStopClass *klass);
@@ -135,8 +138,8 @@ sp_stop_set(SPObject *object, unsigned key, gchar const *value)
          * We need presentation attributes etc.
          * \par
          * remove the hackish "style reading" from here: see comments in
-         * sp_object_get_style_property about the bugs in our current 
-         * approach.  However, note that SPStyle doesn't currently have 
+         * sp_object_get_style_property about the bugs in our current
+         * approach.  However, note that SPStyle doesn't currently have
          * stop-color and stop-opacity properties.
          */
             {
@@ -144,7 +147,7 @@ sp_stop_set(SPObject *object, unsigned key, gchar const *value)
                 if (streq(p, "currentColor")) {
                     stop->currentColor = true;
                 } else {
-                    guint32 const color = sp_svg_read_color(p, 0);                    
+                    guint32 const color = sp_svg_read_color(p, 0);
                     sp_color_set_rgb_rgba32(&stop->specified_color, color);
                 }
             }
@@ -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.
  */
@@ -844,7 +893,7 @@ sp_gradient_repr_clear_vector(SPGradient *gr)
 }
 
 /**
- * Writes the gradient's internal vector (whether from its own stops, or 
+ * Writes the gradient's internal vector (whether from its own stops, or
  * inherited from refs) into the gradient repr as svg:stop elements.
  */
 void
@@ -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. */
@@ -1073,7 +1123,7 @@ sp_gradient_ensure_colors(SPGradient *gr)
 
 /**
  * Renders gradient vector to buffer as line.
- * 
+ *
  * RGB buffer background should be set up beforehand.
  *
  * @param len,width,height,rowstride Buffer parameters (1 or 2 dimensional).
@@ -1149,7 +1199,7 @@ sp_gradient_render_vector_block_rgba(SPGradient *const gradient, guchar *buf,
 
 /**
  * Render rectangular RGB area from gradient vector.
- */ 
+ */
 void
 sp_gradient_render_vector_block_rgb(SPGradient *gradient, guchar *buf,
                                     gint const width, gint const height, gint const rowstride,
@@ -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)
@@ -1402,7 +1453,7 @@ sp_lineargradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags
 
 /**
  * Create linear gradient context.
- * 
+ *
  * Basically we have to deal with transformations
  *
  * 1) color2norm - maps point in (0,NCOLORS) vector to (0,1) vector
@@ -1410,10 +1461,10 @@ sp_lineargradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags
  * 2) gradientTransform
  * 3) bbox2user
  * 4) ctm == userspace to pixel grid
- * 
+ *
  * See also (*) in sp-pattern about why we may need parent_transform.
- * 
- * \todo (point 1 above) fixme: I do not know how to deal with start > 0 
+ *
+ * \todo (point 1 above) fixme: I do not know how to deal with start > 0
  * and end < 1.
  */
 static SPPainter *
@@ -1435,10 +1486,10 @@ sp_lineargradient_painter_new(SPPaintServer *ps,
     lgp->lg = lg;
 
     /** \todo
-     * Technically speaking, we map NCOLORS on line [start,end] onto line 
-     * [0,1].  I almost think we should fill color array start and end in 
-     * that case. The alternative would be to leave these just empty garbage 
-     * or something similar. Originally I had 1023.9999 here - not sure 
+     * Technically speaking, we map NCOLORS on line [start,end] onto line
+     * [0,1].  I almost think we should fill color array start and end in
+     * that case. The alternative would be to leave these just empty garbage
+     * or something similar. Originally I had 1023.9999 here - not sure
      * whether we have really to cut out ceil int (Lauris).
      */
     NR::Matrix color2norm(NR::identity());
@@ -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);
@@ -1723,7 +1775,7 @@ sp_radialgradient_painter_new(SPPaintServer *ps,
 
     if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
         /** \todo
-         * fixme: We may try to normalize here too, look at 
+         * fixme: We may try to normalize here too, look at
          * linearGradient (Lauris)
          */
 
@@ -1736,7 +1788,7 @@ sp_radialgradient_painter_new(SPPaintServer *ps,
     } else {
         /** \todo
          * Problem: What to do, if we have mixed lengths and percentages?
-         * Currently we do ignore percentages at all, but that is not 
+         * Currently we do ignore percentages at all, but that is not
          * good (lauris)
          */