Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / sp-filter.cpp
index 116b132bc8f40f4e27459a9ac92e49215b980cf1..cfd6253a3e7c0e64b8b9dd023abfad2e95ea0c92 100644 (file)
@@ -1,13 +1,14 @@
-#define __SP_FILTER_CPP__
-
 /** \file
  * SVG <filter> implementation.
  */
 /*
  * Authors:
  *   Hugo Rodrigues <haa.rodrigues@gmail.com>
+ *   Niko Kiirala <niko@kiirala.com>
+ *   Jon A. Cruz <jon@joncruz.org>
+ *   Abhishek Sharma
  *
- * Copyright (C) 2006 Hugo Rodrigues
+ * Copyright (C) 2006,2007 Authors
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 # include "config.h"
 #endif
 
+#include <map>
+#include <string.h>
+using std::map;
+using std::pair;
+
+#include <gtkmm.h>
+
 #include "attributes.h"
 #include "document.h"
 #include "sp-filter.h"
 #include "sp-filter-reference.h"
 #include "uri.h"
 #include "xml/repr.h"
+#include <cstring>
+#include <string>
 
 #define SP_MACROS_SILENT
 #include "macros.h"
 
-
-/*
- * For debugging purposes only
- */
-void printfilter(SPFilter *filter)
-{
-       if(filter->filterUnits==SP_FILTER_UNITS_USERSPACEONUSE)
-               g_print("filterUnits=SP_FILTER_UNITS_USERSPACEONUSE\n");
-       else if(filter->filterUnits==SP_FILTER_UNITS_OBJECTBOUNDINGBOX)
-               g_print("filterUnits=SP_FILTER_UNITS_OBJECTBOUNDINGBOX\n");
-       else
-               g_print("filterUnits=UNKNOWN!!!\n");
-
-       if(filter->primitiveUnits==SP_FILTER_UNITS_USERSPACEONUSE)
-               g_print("primitiveUnits=SP_FILTER_UNITS_USERSPACEONUSE\n");
-       else if(filter->primitiveUnits==SP_FILTER_UNITS_OBJECTBOUNDINGBOX)
-               g_print("primitiveUnits=SP_FILTER_UNITS_OBJECTBOUNDINGBOX\n");
-       else
-               g_print("primitiveUnits=UNKNOWN!!!\n");
-
-//TODO: print X, Y, W and H units
-       g_print("x=%lf\n", filter->x.computed);
-       g_print("y=%lf\n", filter->y.computed);
-       g_print("width=%lf\n", filter->width.computed);
-       g_print("height=%lf\n", filter->height.computed);
-       g_print("filterRes=(%lf %lf)\n", filter->filterRes.getNumber(), filter->filterRes.getOptNumber());
-
-}
-
-
+#include "display/nr-filter.h"
 
 /* Filter base class */
 
@@ -66,10 +47,14 @@ static void sp_filter_build(SPObject *object, SPDocument *document, Inkscape::XM
 static void sp_filter_release(SPObject *object);
 static void sp_filter_set(SPObject *object, unsigned int key, gchar const *value);
 static void sp_filter_update(SPObject *object, SPCtx *ctx, guint flags);
-static Inkscape::XML::Node *sp_filter_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
+static void sp_filter_child_added(SPObject *object,
+                                    Inkscape::XML::Node *child,
+                                    Inkscape::XML::Node *ref);
+static void sp_filter_remove_child(SPObject *object, Inkscape::XML::Node *child);
+static Inkscape::XML::Node *sp_filter_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
 
 static void filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter);
-static void filter_ref_modified(SPObject *href, SPFilter *filter);
+static void filter_ref_modified(SPObject *href, guint flags, SPFilter *filter);
 
 static SPObjectClass *filter_parent_class;
 
@@ -107,6 +92,8 @@ sp_filter_class_init(SPFilterClass *klass)
     sp_object_class->write = sp_filter_write;
     sp_object_class->set = sp_filter_set;
     sp_object_class->update = sp_filter_update;
+    sp_object_class->child_added = sp_filter_child_added;
+    sp_object_class->remove_child = sp_filter_remove_child;
 }
 
 static void
@@ -121,10 +108,19 @@ sp_filter_init(SPFilter *filter)
     filter->height = 0;
 
     filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
-    filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+    filter->primitiveUnits = SP_FILTER_UNITS_USERSPACEONUSE;
     filter->filterUnits_set = FALSE;
     filter->primitiveUnits_set = FALSE;
 
+    filter->_renderer = NULL;
+
+    filter->_image_name = new std::map<gchar *, int, ltstr>;
+    filter->_image_name->clear();
+    filter->_image_number_next = 0;
+
+    filter->filterRes = NumberOptNumber();
+
+    new (&filter->modified_connection) sigc::connection();
 }
 
 /**
@@ -140,17 +136,17 @@ sp_filter_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *rep
     }
 
     //Read values of key attributes from XML nodes into object.
-    sp_object_read_attr(object, "filterUnits");
-    sp_object_read_attr(object, "primitiveUnits");
-    sp_object_read_attr(object, "x");
-    sp_object_read_attr(object, "y");
-    sp_object_read_attr(object, "width");
-    sp_object_read_attr(object, "height");
-    sp_object_read_attr(object, "filterRes");
-    sp_object_read_attr(object, "xlink:href");
+    object->readAttr( "filterUnits" );
+    object->readAttr( "primitiveUnits" );
+    object->readAttr( "x" );
+    object->readAttr( "y" );
+    object->readAttr( "width" );
+    object->readAttr( "height" );
+    object->readAttr( "filterRes" );
+    object->readAttr( "xlink:href" );
 
 //is this necessary?
-    sp_document_add_resource(document, "filter", object);
+    document->addResource("filter", object);
 }
 
 /**
@@ -163,21 +159,22 @@ sp_filter_release(SPObject *object)
 
     if (SP_OBJECT_DOCUMENT(object)) {
         /* Unregister ourselves */
-        sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "filter", SP_OBJECT(object));
+        SP_OBJECT_DOCUMENT(object)->removeResource("filter", SP_OBJECT(object));
     }
 
 //TODO: release resources here
 
     //release href
     if (filter->href) {
-        if (filter->href->getObject()) {
-            sp_signal_disconnect_by_data(filter->href->getObject(), filter);
-        }
+        filter->modified_connection.disconnect();
         filter->href->detach();
         delete filter->href;
         filter->href = NULL;
     }
 
+    filter->modified_connection.~connection();
+    delete filter->_image_name;
+
     if (((SPObjectClass *) filter_parent_class)->release)
         ((SPObjectClass *) filter_parent_class)->release(object);
 }
@@ -207,36 +204,37 @@ sp_filter_set(SPObject *object, unsigned int key, gchar const *value)
             break;
         case SP_ATTR_PRIMITIVEUNITS:
             if (value) {
-                if (!strcmp(value, "userSpaceOnUse")) {
-                    filter->primitiveUnits = SP_FILTER_UNITS_USERSPACEONUSE;
-                } else {
+                if (!strcmp(value, "objectBoundingBox")) {
                     filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+                } else {
+                    filter->primitiveUnits = SP_FILTER_UNITS_USERSPACEONUSE;
                 }
                 filter->primitiveUnits_set = TRUE;
             } else {
-                filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+                filter->primitiveUnits = SP_FILTER_UNITS_USERSPACEONUSE;
                 filter->primitiveUnits_set = FALSE;
             }
             object->requestModified(SP_OBJECT_MODIFIED_FLAG);
             break;
-       case SP_ATTR_X:
+        case SP_ATTR_X:
             filter->x.readOrUnset(value);
-           object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+            object->requestModified(SP_OBJECT_MODIFIED_FLAG);
             break;
-       case SP_ATTR_Y:
-           filter->y.readOrUnset(value);
-           object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+        case SP_ATTR_Y:
+            filter->y.readOrUnset(value);
+            object->requestModified(SP_OBJECT_MODIFIED_FLAG);
             break;
-       case SP_ATTR_WIDTH:
-           filter->width.readOrUnset(value);
-           object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+        case SP_ATTR_WIDTH:
+            filter->width.readOrUnset(value);
+            object->requestModified(SP_OBJECT_MODIFIED_FLAG);
             break;
-       case SP_ATTR_HEIGHT:
-           filter->height.readOrUnset(value);
-           object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+        case SP_ATTR_HEIGHT:
+            filter->height.readOrUnset(value);
+            object->requestModified(SP_OBJECT_MODIFIED_FLAG);
             break;
-       case SP_ATTR_FILTERRES:
-               filter->filterRes.set(value);
+        case SP_ATTR_FILTERRES:
+            filter->filterRes.set(value);
+            object->requestModified(SP_OBJECT_MODIFIED_FLAG);
             break;
         case SP_ATTR_XLINK_HREF:
             if (value) {
@@ -251,7 +249,7 @@ sp_filter_set(SPObject *object, unsigned int key, gchar const *value)
             }
             break;
         default:
-            // See if any parents need this value. 
+            // See if any parents need this value.
             if (((SPObjectClass *) filter_parent_class)->set) {
                 ((SPObjectClass *) filter_parent_class)->set(object, key, value);
             }
@@ -283,12 +281,31 @@ sp_filter_update(SPObject *object, SPCtx *ctx, guint flags)
  * Writes its settings to an incoming repr object, if any.
  */
 static Inkscape::XML::Node *
-sp_filter_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
+sp_filter_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
 {
     SPFilter *filter = SP_FILTER(object);
 
-    if (!repr) {
-        repr = SP_OBJECT_REPR(object)->duplicate();
+    // Original from sp-item-group.cpp
+    if (flags & SP_OBJECT_WRITE_BUILD) {
+        if (!repr) {
+            repr = doc->createElement("svg:filter");
+        }
+        GSList *l = NULL;
+        for ( SPObject *child = object->firstChild(); child; child = child->getNext() ) {
+            Inkscape::XML::Node *crepr = child->updateRepr(doc, NULL, flags);
+            if (crepr) {
+                l = g_slist_prepend (l, crepr);
+            }
+        }
+        while (l) {
+            repr->addChild((Inkscape::XML::Node *) l->data, NULL);
+            Inkscape::GC::release((Inkscape::XML::Node *) l->data);
+            l = g_slist_remove (l, l->data);
+        }
+    } else {
+        for ( SPObject *child = object->firstChild() ; child; child = child->getNext() ) {
+            child->updateRepr(flags);
+        }
     }
 
     if ((flags & SP_OBJECT_WRITE_ALL) || filter->filterUnits_set) {
@@ -304,11 +321,11 @@ sp_filter_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
 
     if ((flags & SP_OBJECT_WRITE_ALL) || filter->primitiveUnits_set) {
         switch (filter->primitiveUnits) {
-            case SP_FILTER_UNITS_USERSPACEONUSE:
-                repr->setAttribute("primitiveUnits", "userSpaceOnUse");
+            case SP_FILTER_UNITS_OBJECTBOUNDINGBOX:
+                repr->setAttribute("primitiveUnits", "objectBoundingBox");
                 break;
             default:
-                repr->setAttribute("primitiveUnits", "objectBoundingBox");
+                repr->setAttribute("primitiveUnits", "userSpaceOnUse");
                 break;
         }
     }
@@ -337,9 +354,10 @@ sp_filter_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
         repr->setAttribute("height", NULL);
     }
 
-    if (filter->filterRes._set) {
-       char filterRes[32];
-        repr->setAttribute("filterRes", filter->filterRes.getValueString(filterRes));
+    if (filter->filterRes.getNumber()>=0) {
+        gchar *tmp = filter->filterRes.getValueString();
+        repr->setAttribute("filterRes", tmp);
+        g_free(tmp);
     } else {
         repr->setAttribute("filterRes", NULL);
     }
@@ -350,8 +368,17 @@ sp_filter_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
         g_free(uri_string);
     }
 
+    // TODO: This is evil, correctly implement support for color-interpolation-filters!!!
+    // The color-interpolation-filters attribute is initially set to linearRGB according to the SVG standard.
+    // However, Inkscape completely ignores it and implicitly assumes that it is sRGB (like color-interpolation-filters).
+    // This results in a discrepancy between Inkscape and other renderers in how they render filters.
+    // To mitigate this problem I've (Jasper van de Gronde,th.v.d.gronde@hccnet.nl) added this to ensure that at least
+    // any filters written by Inkscape will henceforth be rendered the same in other renderers.
+    // In the future Inkscape should have proper support for the color-interpolation properties and this should be changed.
+    repr->setAttribute("color-interpolation-filters", "sRGB");
+
     if (((SPObjectClass *) filter_parent_class)->write) {
-        ((SPObjectClass *) filter_parent_class)->write(object, repr, flags);
+        ((SPObjectClass *) filter_parent_class)->write(object, doc, repr, flags);
     }
 
     return repr;
@@ -365,23 +392,186 @@ static void
 filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter)
 {
     if (old_ref) {
-        sp_signal_disconnect_by_data(old_ref, filter);
+        filter->modified_connection.disconnect();
     }
     if ( SP_IS_FILTER(ref)
          && ref != filter )
     {
-        g_signal_connect(G_OBJECT(ref), "modified", G_CALLBACK(filter_ref_modified), filter);
+        filter->modified_connection =
+            ref->connectModified(sigc::bind(sigc::ptr_fun(&filter_ref_modified), filter));
     }
 
-    filter_ref_modified(ref, filter);
+    filter_ref_modified(ref, 0, filter);
 }
 
 static void
-filter_ref_modified(SPObject *href, SPFilter *filter)
+filter_ref_modified(SPObject */*href*/, guint /*flags*/, SPFilter *filter)
 {
     SP_OBJECT(filter)->requestModified(SP_OBJECT_MODIFIED_FLAG);
 }
 
+/**
+ * Callback for child_added event.
+ */
+static void
+sp_filter_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
+{
+    //SPFilter *f = SP_FILTER(object);
+
+    if (((SPObjectClass *) filter_parent_class)->child_added)
+        (* ((SPObjectClass *) filter_parent_class)->child_added)(object, child, ref);
+
+    object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
+/**
+ * Callback for remove_child event.
+ */
+static void
+sp_filter_remove_child(SPObject *object, Inkscape::XML::Node *child)
+{
+//    SPFilter *f = SP_FILTER(object);
+
+    if (((SPObjectClass *) filter_parent_class)->remove_child)
+        (* ((SPObjectClass *) filter_parent_class)->remove_child)(object, child);
+
+    object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
+void sp_filter_build_renderer(SPFilter *sp_filter, Inkscape::Filters::Filter *nr_filter)
+{
+    g_assert(sp_filter != NULL);
+    g_assert(nr_filter != NULL);
+
+    sp_filter->_renderer = nr_filter;
+
+    nr_filter->set_filter_units(sp_filter->filterUnits);
+    nr_filter->set_primitive_units(sp_filter->primitiveUnits);
+    nr_filter->set_x(sp_filter->x);
+    nr_filter->set_y(sp_filter->y);
+    nr_filter->set_width(sp_filter->width);
+    nr_filter->set_height(sp_filter->height);
+
+    if (sp_filter->filterRes.getNumber() >= 0) {
+        if (sp_filter->filterRes.getOptNumber() >= 0) {
+            nr_filter->set_resolution(sp_filter->filterRes.getNumber(),
+                                      sp_filter->filterRes.getOptNumber());
+        } else {
+            nr_filter->set_resolution(sp_filter->filterRes.getNumber());
+        }
+    }
+
+    nr_filter->clear_primitives();
+    SPObject *primitive_obj = sp_filter->children;
+    while (primitive_obj) {
+        if (SP_IS_FILTER_PRIMITIVE(primitive_obj)) {
+            SPFilterPrimitive *primitive = SP_FILTER_PRIMITIVE(primitive_obj);
+            g_assert(primitive != NULL);
+            if (((SPFilterPrimitiveClass*) G_OBJECT_GET_CLASS(primitive))->build_renderer) {
+                ((SPFilterPrimitiveClass *) G_OBJECT_GET_CLASS(primitive))->build_renderer(primitive, nr_filter);
+            } else {
+                g_warning("Cannot build filter renderer: missing builder");
+            }
+        }
+        primitive_obj = primitive_obj->next;
+    }
+}
+
+int sp_filter_primitive_count(SPFilter *filter) {
+    g_assert(filter != NULL);
+    int count = 0;
+
+    SPObject *primitive_obj = filter->children;
+    while (primitive_obj) {
+        if (SP_IS_FILTER_PRIMITIVE(primitive_obj)) count++;
+        primitive_obj = primitive_obj->next;
+    }
+    return count;
+}
+
+int sp_filter_get_image_name(SPFilter *filter, gchar const *name) {
+    gchar *name_copy = strdup(name);
+    map<gchar *, int, ltstr>::iterator result = filter->_image_name->find(name_copy);
+    free(name_copy);
+    if (result == filter->_image_name->end()) return -1;
+    else return (*result).second;
+}
+
+int sp_filter_set_image_name(SPFilter *filter, gchar const *name) {
+    int value = filter->_image_number_next;
+    filter->_image_number_next++;
+    gchar *name_copy = strdup(name);
+    pair<gchar*,int> new_pair(name_copy, value);
+    pair<map<gchar*,int,ltstr>::iterator,bool> ret = filter->_image_name->insert(new_pair);
+    if (ret.second == false) {
+        return (*ret.first).second;
+    }
+    return value;
+}
+
+gchar const *sp_filter_name_for_image(SPFilter const *filter, int const image) {
+    switch (image) {
+        case Inkscape::Filters::NR_FILTER_SOURCEGRAPHIC:
+            return "SourceGraphic";
+            break;
+        case Inkscape::Filters::NR_FILTER_SOURCEALPHA:
+            return "SourceAlpha";
+            break;
+        case Inkscape::Filters::NR_FILTER_BACKGROUNDIMAGE:
+            return "BackgroundImage";
+            break;
+        case Inkscape::Filters::NR_FILTER_BACKGROUNDALPHA:
+            return "BackgroundAlpha";
+            break;
+        case Inkscape::Filters::NR_FILTER_STROKEPAINT:
+            return "StrokePaint";
+            break;
+        case Inkscape::Filters::NR_FILTER_FILLPAINT:
+            return "FillPaint";
+            break;
+        case Inkscape::Filters::NR_FILTER_SLOT_NOT_SET:
+        case Inkscape::Filters::NR_FILTER_UNNAMED_SLOT:
+            return 0;
+            break;
+        default:
+            for (map<gchar *, int, ltstr>::const_iterator i
+                     = filter->_image_name->begin() ;
+                 i != filter->_image_name->end() ; i++) {
+                if (i->second == image) {
+                    return i->first;
+                }
+            }
+    }
+    return 0;
+}
+
+Glib::ustring sp_filter_get_new_result_name(SPFilter *filter) {
+    g_assert(filter != NULL);
+    int largest = 0;
+
+    SPObject *primitive_obj = filter->children;
+    while (primitive_obj) {
+        if (SP_IS_FILTER_PRIMITIVE(primitive_obj)) {
+            Inkscape::XML::Node *repr = SP_OBJECT_REPR(primitive_obj);
+            char const *result = repr->attribute("result");
+            int index;
+            if (result && sscanf(result, "result%d", &index) == 1) {
+                if (index > largest) {
+                    largest = index;
+                }
+            }
+        }
+        primitive_obj = primitive_obj->next;
+    }
+
+    return "result" + Glib::Ascii::dtostr(largest + 1);
+}
+
+bool ltstr::operator()(const char* s1, const char* s2) const
+{
+    return strcmp(s1, s2) < 0;
+}
+
 
 /*
   Local Variables:
@@ -392,4 +582,4 @@ filter_ref_modified(SPObject *href, SPFilter *filter)
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :