Code

svg-filters branch merged back to head
authorkiirala <kiirala@users.sourceforge.net>
Wed, 21 Jun 2006 16:04:22 +0000 (16:04 +0000)
committerkiirala <kiirala@users.sourceforge.net>
Wed, 21 Jun 2006 16:04:22 +0000 (16:04 +0000)
35 files changed:
doc/nr-filter-interface.txt [new file with mode: 0644]
src/Makefile_insert
src/attributes.cpp
src/attributes.h
src/display/Makefile_insert
src/display/nr-arena-glyphs.h
src/display/nr-arena-group.cpp
src/display/nr-arena-group.h
src/display/nr-arena-item.cpp
src/display/nr-arena-item.h
src/display/nr-arena-shape.cpp
src/display/nr-filter-gaussian.cpp [new file with mode: 0644]
src/display/nr-filter-gaussian.h [new file with mode: 0644]
src/display/nr-filter-primitive.cpp [new file with mode: 0644]
src/display/nr-filter-primitive.h [new file with mode: 0644]
src/display/nr-filter-types.h [new file with mode: 0644]
src/display/nr-filter.cpp [new file with mode: 0644]
src/display/nr-filter.h [new file with mode: 0644]
src/libnr/nr-rect.cpp
src/libnr/nr-rect.h
src/number-opt-number.h [new file with mode: 0644]
src/sp-filter-fns.h [new file with mode: 0644]
src/sp-filter-reference.cpp [new file with mode: 0644]
src/sp-filter-reference.h [new file with mode: 0644]
src/sp-filter-units.h [new file with mode: 0644]
src/sp-filter.cpp [new file with mode: 0644]
src/sp-filter.h [new file with mode: 0644]
src/sp-gaussian-blur-fns.h [new file with mode: 0644]
src/sp-gaussian-blur.cpp [new file with mode: 0644]
src/sp-gaussian-blur.h [new file with mode: 0644]
src/sp-item-group.cpp
src/sp-object-repr.cpp
src/sp-text.cpp
src/style.cpp
src/style.h

diff --git a/doc/nr-filter-interface.txt b/doc/nr-filter-interface.txt
new file mode 100644 (file)
index 0000000..d5eb84a
--- /dev/null
@@ -0,0 +1,229 @@
+Public interface for NR::Filter and NR::FilterPrimitive
+
+Constructors
+============
+
+Filter::Filter()
+Creates a new filter with space for one filter element
+
+Filter::Filter(int n)
+Creates a new filter with space for n filter elements. If number of filter
+elements is known beforehand, it's better to use this constructor.
+
+
+Managing filter primitives
+==========================
+
+FilterPrimitive * Filter::add_primitive(FilterPrimitiveType type)
+Creates a new filter primitive under this filter object.
+New primitive is placed so that it will be executed after all filter
+primitives defined beforehand for this filter object.
+Should this filter not have enough space for a new primitive, the filter
+is enlarged to accomodate the new filter element. It may be enlarged by more
+that one element.
+Returns a pointer to the filter primitive created.
+Returns NULL, if type is not valid filter primitive type or filter primitive
+of such type cannot be created.
+
+void Filter::clear_primitives()
+Removes all filter primitives from this filter.
+All pointers to filter primitives inside this filter should be considered
+invalid after calling this function.
+
+FilterPrimitive * Filter::replace_primitive(FilterPrimitive *target,
+                                            FilterPrimitiveType type)
+Replaces filter primitive pointed by 'target' with a new filter primitive of
+type 'type'
+If 'target' does not correspond to any primitive inside this filter OR
+'type' is not a valid filter primitive type OR
+filter primitive of such type cannot be created,
+this function returns NULL and doesn't change the internal state of this
+filter.
+Otherwise, a new filter primitive is created. Any pointers to filter primitive
+'target' should be considered invalid. A pointer to the newly created
+primitive is returned.
+
+
+Filter primitive types
+======================
+
+enum FilterPrimitiveType is declared in display/nr-filter-types.h
+
+Enumeration value              Corresponding filter primitive
+NR_FILTER_BLEND                        feBlend
+NR_FILTER_COLORMATRIX          feColorMatrix
+NR_FILTER_COMPONENTTRANSFER    feComponentTransfer
+NR_FILTER_COMPOSITE            feComposite
+NR_FILTER_CONVOLVEMATRIX       feConvolveMatrix
+NR_FILTER_DIFFUSELIGHTING      feDiffuseLighting
+NR_FILTER_DISPLACEMENTMAP      feDisplacementMap
+NR_FILTER_FLOOD                        feFlood
+NR_FILTER_GAUSSIANBLUR         feGaussianBlur
+NR_FILTER_IMAGE                        feImage
+NR_FILTER_MERGE                        feMerge
+NR_FILTER_MORPHOLOGY           feMorphology
+NR_FILTER_OFFSET               feOffset
+NR_FILTER_SPECULARLIGHTING     feSpecularLighting
+NR_FILTER_TILE                 feTile
+NR_FILTER_TURBULENCE           feTurbulence
+
+
+Setting inputs and outputs for filter primitives
+================================================
+
+Each filter primitive can take one or more images as inputs and produces
+a single image as output. In NR::Filter these are managed as image slots.
+Every slot can hold one image.
+
+There are two types of slots: pre-defined and user defined. Pre-defined
+slots may only be used as inputs, while user defined slots may be used as
+both inputs and outputs. User defined slots are numbered from 0 upwards,
+pre-defined slots are numbered with the following constants:
+
+Constant name                  Corresponding SVG input name
+NR_FILTER_SOURCEGRAPHIC                SourceGraphic
+NR_FILTER_SOURCEALPHA          SourceAlpha
+NR_FILTER_BACKGROUNDIMAGE      BackgroundImage
+NR_FILTER_BACKGROUNDAPLHA      BackgroundAlpha
+NR_FILTER_FILLPAINT            FillPaint
+NR_FILTER_SOURCEPAINT          SourcePaint
+(defined in display/nr-filter-types.h)
+
+Any user defined slot used as input must be the output of some previous
+filter primitive. Other than that, user defined input slots do not need to be
+used in any particular order.
+
+void FilterPrimitive::set_input(int slot)
+Sets the input slot number 'slot' to be used as input in rendering filter
+primitive 'primitive'
+For filter primitive types accepting more than one input, this sets the
+first input.
+If any of the required input slots is not set, the output of previous filter
+primitive is used, or SourceGraphic if this is the first primitive for this
+filter.
+
+void FilterPrimitive::set_input(int input, int slot)
+Sets the input slot number 'slot' to be user as input number 'input' in
+rendering filter primitive 'primitive'
+First input for a filter primitive is number 0. For primitives with attributes
+'in' and 'in2', these are numbered 0 and 1, respectively.
+If any of required input slots for a filter is not set, the output of
+previous filter primitive is used, or SourceGraphic if this is the first
+filter primitive for this filter.
+
+void FilterPrimitive::set_output(int slot)
+Sets the slot number 'slot' to be used as output from filter primitive
+'primitive'
+If output slot for a filter element is not set, one of the unused image
+slots is used.
+It is an error to specify a pre-defined slot as 'slot'. Such call does
+not have any effect to the state of filter or its primitives.
+
+void Filter::set_output(int slot)
+Sets the slot number 'slot' to be used as result from this filter.
+If output is not set, the output from last filter primitive is used as
+output from the filter.
+It is an error to specify a pre-defined slot as 'slot'. Such call does
+not have any effect to the state of filter or its primitives.
+
+
+Functions for reading filter state
+==================================
+
+int Filter::get_enlarge(Matrix const &m)
+Returns the amount of pixels the rendering area should be enlarged
+to prevent visual artefacts when filter needs to read pixels that
+are outside its output area (e.g. gaussian blur)
+
+void Fiter::bbox_enlarge(NRRectL &bbox)
+Given an object bounding box, this function enlarges it so that it
+contains the filter effect area
+
+
+Filter effects region and filter primitive subregion
+====================================================
+
+void Filter::set_x(SVGLength &lenght)
+void FilterPrimitive::set_x(SVGLength &length)
+
+void Filter::set_y(SVGLength &length)
+void FilterPrimitive::set_y(SVGLength &length)
+
+void Filter::set_width(SVGLength &length)
+void FilterPrimitive::set_width(SVGLength &length)
+
+void Filter::set_height(SVGLength &length)
+void FilterPrimitive::set_width(SVGLength &length)
+
+These functions set the parameters for filter effects region and filter 
+primitive subregion.
+Passing an unset length (length._set == false) results in no changes to 
+filter state.
+Filter will not hold any references to the passed SVGLength object after 
+function returns.
+If any of these parameters does not get set - either because function 
+for setting that is not called, or it is called with an unset length -  
+the default value, as defined in SVG standard, for that parameter is 
+used instead.
+
+void Filter::set_region(SVGLength &x, SVGLength &y,
+                        SVGLength &width, SVGLength &height)
+void FilterPrimitive::set_region(SVGLength &x, SVGLength &y,
+                                 SVGLength &width, SVGLength &height)
+
+This is shorthand for calling set_x(x), set_y(y), set_width(width) and 
+set_height(height). The result is as if those four functions had been 
+called separately.
+
+void Filter::reset_region()
+void FilterPrimitive::reset_region()
+
+Resets the filter effects region or filter primitive subregion to its 
+default value as defined in SVG standard.
+
+void Filter::set_resolution(double x_pixels)
+
+Sets the width of intermediate images in pixels. If not set, suitable 
+resolution is determined automatically. If x_pixels is less than zero, 
+calling this function results in no changes to filter state.
+
+void Filter::set_resolution(double x_pixels, double y_pixels)
+
+Sets the width and height of intermediate images in pixels. If not set, 
+suitable resolution is determined automatically. If either parameter is 
+less than zero, calling this function results in no changes to filter 
+state.
+
+void Filter::reset_resolution()
+
+Resets the filter resolution to its default value, i.e. automatically 
+determined.
+
+void Filter::set_filter_units(SPFilterUnits unit)
+void Filter::set_primitive_units(SPFilterUnits unit)
+
+Set the filterUnits and primitiveUnits -properteries, respectively. If 
+not set, the default values are used: objectBoundingBox for filterUnits 
+and userSpaceOnUse for primitiveUnits. If the parameter value is not a 
+valid enumeration value from SPFilterUnits, no changes to filter state 
+are made.
+
+
+Parameters specific to some filter primitive type
+=================================================
+
+Gaussian blur
+-------------
+
+void FilterGaussian::set_deviation(double deviation)
+void FilterGaussian::set_deviation(double x, double y)
+
+Set the standard deviation value for gaussian blur. One-parameter 
+version sets deviation along both axis to same value, two-parameter 
+version allows setting deviation along x- and y-axis separately.
+Passing either of these functions a negative value, NaN or infinity is 
+considered an error and no changes to filter state are made. If not set, 
+default value of zero is used, which means the filter results in 
+transparent black image.
+(NB: as for now, the default value can be overriden with configuration 
+file parameter options.filtertest)
index ceecbd9ce6fdc51524e96aab19f73b7917062f05..a0f2ff527be1728bf8ed2bf3cfa5a01f393229a5 100644 (file)
@@ -141,9 +141,16 @@ libinkpre_a_SOURCES =      \
        sp-cursor.cpp sp-cursor.h       \
        sp-defs.cpp sp-defs.h   \
        sp-ellipse.cpp sp-ellipse.h     \
+       sp-filter-fns.h \
+       sp-filter-reference.cpp       \
+       sp-filter-reference.h       \
+        sp-filter-units.h       \
+       sp-filter.cpp sp-filter.h       \
        sp-flowdiv.h sp-flowdiv.cpp \
        sp-flowregion.h sp-flowregion.cpp \
        sp-flowtext.h sp-flowtext.cpp \
+       sp-gaussian-blur.cpp sp-gaussian-blur.h \
+       sp-gaussian-blur-fns.h  \
        sp-gradient-fns.h       \
        sp-gradient-reference.cpp       \
        sp-gradient-reference.h \
index 541acfc0cfda10c5752e77a823001e270fe489dd..f0ecc1e415ed13bccc0ac252997375062732699e 100644 (file)
@@ -171,6 +171,12 @@ static SPStyleProp const props[] = {
     {SP_ATTR_STARTOFFSET, "startOffset"},
     /* SPStop */
     {SP_ATTR_OFFSET, "offset"},
+    /* SPFilter */
+    {SP_ATTR_FILTERUNITS, "filterUnits"},
+    {SP_ATTR_PRIMITIVEUNITS, "primitiveUnits"},
+    {SP_ATTR_FILTERRES, "filterRes"},
+    /* SPGaussianBlur */
+    {SP_ATTR_STDDEVIATION, "stdDeviation"},
     /* SPGradient */
     {SP_ATTR_GRADIENTUNITS, "gradientUnits"},
     {SP_ATTR_GRADIENTTRANSFORM, "gradientTransform"},
index bcbed50d0f45aedb3d4bf96189cc8dbe128f44ca..d80e870061defe57d975679914e022f5da32e4c8 100644 (file)
@@ -171,6 +171,12 @@ enum SPAttributeEnum {
     SP_ATTR_STARTOFFSET,
     /* SPStop */
     SP_ATTR_OFFSET,
+    /* SPFilter */
+    SP_ATTR_FILTERUNITS,
+    SP_ATTR_PRIMITIVEUNITS,
+    SP_ATTR_FILTERRES,
+    /* SPGaussianBlur */
+    SP_ATTR_STDDEVIATION,
     /* SPGradient */
     SP_ATTR_GRADIENTUNITS,
     SP_ATTR_GRADIENTTRANSFORM,
index de6ec85c2a688cfc0bba5d8cf28dc071e5f082c8..5aec6894f2d28611854769056ce7002e3c744672 100644 (file)
@@ -60,7 +60,13 @@ display_libspdisplay_a_SOURCES = \
        display/sp-ctrlline.cpp \
        display/sp-ctrlline.h \
        display/sp-ctrlquadr.cpp \
-       display/sp-ctrlquadr.h 
+       display/sp-ctrlquadr.h \
+       display/nr-filter.cpp           \
+       display/nr-filter.h             \
+       display/nr-filter-primitive.cpp \
+       display/nr-filter-primitive.h   \
+       display/nr-filter-gaussian.cpp  \
+       display/nr-filter-gaussian.h
 
 display_bezier_utils_test_SOURCES = display/bezier-utils-test.cpp
 display_bezier_utils_test_LDADD = libnr/libnr.a -lglib-2.0
index 23b74a3782fd288b3e144b681e1e988e4303842e..a2dda988b5b59fa58b1f3b23615661a6d63f7563 100644 (file)
@@ -79,7 +79,7 @@ typedef struct NRArenaGlyphsGroupClass NRArenaGlyphsGroupClass;
 NRType nr_arena_glyphs_group_get_type (void);
 
 struct NRArenaGlyphsGroup : public NRArenaGroup {
-       SPStyle *style;
+  //SPStyle *style;
        NRRect paintbox;
        /* State data */
        SPPainter *fill_painter;
index f15c72e8e31394392abaf2860a001e93163f4310..9657715ea72a874c8b7303952429e590e63eaac9 100644 (file)
@@ -12,7 +12,9 @@
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
-#include "nr-arena-group.h"
+#include "display/nr-arena-group.h"
+#include "display/nr-filter.h"
+#include "style.h"
 
 static void nr_arena_group_class_init (NRArenaGroupClass *klass);
 static void nr_arena_group_init (NRArenaGroup *group);
@@ -75,6 +77,7 @@ nr_arena_group_init (NRArenaGroup *group)
        group->transparent = FALSE;
        group->children = NULL;
        group->last = NULL;
+       group->style = NULL;
        nr_matrix_set_identity (&group->child_transform);
   
 #ifdef arena_item_tile_cache
@@ -181,6 +184,20 @@ nr_arena_group_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigned int
        return beststate;
 }
 
+void nr_arena_group_set_style (NRArenaGroup *group, SPStyle *style)
+{
+  g_return_if_fail(group != NULL);
+  g_return_if_fail(NR_IS_ARENA_GROUP(group));
+
+  if (style) sp_style_ref(style);
+  if (group->style) sp_style_unref(group->style);
+  group->style = style;
+
+  if (style && style->filter.set && style->filter.filter) {
+    group->filter = new NR::Filter();
+  }
+}
+
 static unsigned int
 nr_arena_group_render (NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags)
 {
index b33495362003f46f188582578ef05503e8df6bfe..9da16908d2a3531880426922ef6e9f4ea83a7ee2 100644 (file)
@@ -18,6 +18,7 @@
 #define NR_IS_ARENA_GROUP(o) (NR_CHECK_INSTANCE_TYPE ((o), NR_TYPE_ARENA_GROUP))
 
 #include "nr-arena-item.h"
+#include "style.h"
 
 NRType nr_arena_group_get_type (void);
 
@@ -26,6 +27,7 @@ struct NRArenaGroup : public NRArenaItem{
        NRArenaItem *children;
        NRArenaItem *last;
        NRMatrix child_transform;
+        SPStyle *style;
 
        static NRArenaGroup *create(NRArena *arena) {
                NRArenaGroup *obj=reinterpret_cast<NRArenaGroup *>(nr_object_new(NR_TYPE_ARENA_GROUP));
@@ -42,5 +44,6 @@ void nr_arena_group_set_transparent (NRArenaGroup *group, unsigned int transpare
 
 void nr_arena_group_set_child_transform(NRArenaGroup *group, NR::Matrix const &t);
 void nr_arena_group_set_child_transform(NRArenaGroup *group, NRMatrix const *t);
+void nr_arena_group_set_style(NRArenaGroup *group, SPStyle *style);
 
 #endif
index 97c769548773facd00548f88225a191aaad983c7..57413ef227740b750e756026a2429c061726b3b6 100644 (file)
 #include <libnr/nr-pixops.h>
 #include "nr-arena.h"
 #include "nr-arena-item.h"
-//#include "nr-arena-group.h"
 #include "gc-core.h"
 
+#include "nr-filter.h"
+#include "libnr/nr-rect.h"
+#include "nr-arena-group.h"
+
 namespace GC = Inkscape::GC;
 
 static void nr_arena_item_class_init (NRArenaItemClass *klass);
@@ -93,6 +96,7 @@ nr_arena_item_init (NRArenaItem *item)
        item->mask = NULL;
        item->px = NULL;
        item->data = NULL;
+       item->filter = NULL;
 }
 
 static void
@@ -243,10 +247,17 @@ nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc, unsigne
        if (item->transform) {
                nr_matrix_multiply (&childgc.transform, item->transform, &childgc.transform);
        }
+       /* Remember the transformation matrix */
+       item->ctm = childgc.transform;
 
        /* Invoke the real method */
        item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset);
        if (item->state & NR_ARENA_ITEM_STATE_INVALID) return item->state;
+       /* Enlarge the bounding box to contain filter effects */
+       if(item->filter) {
+         item->filter->bbox_enlarge(item->bbox);
+       }
+           
        /* Clipping */
        if (item->clip) {
                unsigned int newstate;
@@ -300,6 +311,10 @@ unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area,
        if (!item->visible) return item->state | NR_ARENA_ITEM_STATE_RENDER;
        nr_rect_l_intersect (&carea, area, &item->bbox);
        if (nr_rect_l_test_empty (&carea)) return item->state | NR_ARENA_ITEM_STATE_RENDER;
+       if(item->filter) {
+         nr_rect_l_enlarge(&carea, item->filter->get_enlarge(item->ctm));
+         nr_rect_l_intersect(&carea, &carea, &item->bbox);
+       }
 
        if (item->px) {
                /* Has cache pixblock, render this and return */
@@ -537,7 +552,7 @@ unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area,
 #endif
   } else {
     /* Determine, whether we need temporary buffer */
-    if (item->clip || item->mask || ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE)) {
+    if (item->clip || item->mask || ((item->opacity != 255) && !item->render_opacity && item->arena->rendermode != RENDERMODE_OUTLINE) || item->filter) {
       NRPixBlock ipb, mpb;
 
       /* Setup and render item buffer */
@@ -553,6 +568,11 @@ unsigned int nr_arena_item_invoke_render(NRArenaItem *item, NRRectL const *area,
       }
       ipb.empty = FALSE;
 
+      /* Run filtering test, if a filter is set for this object */
+      if(item->filter) {
+       item->filter->render(item, &ipb);
+      }
+
       if (item->clip || item->mask) {
         /* Setup mask pixblock */
         nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0, carea.y0, carea.x1, carea.y1, TRUE);
index 22b9929206acfc1be9b9867c3ae8e1ed4d9dbe14..7a6c676024e96cf18c03ab30e7c20addf36c75be 100644 (file)
 #include <libnr/nr-object.h>
 #include "gc-soft-ptr.h"
 #include "nr-arena-forward.h"
+#include "display/nr-filter.h"
+/* TODO: without this, gcc barfs on clause "NR::Filter *filter" later on.
+ * Obviously we shouldn't need to have the next three rows */
+namespace NR {
+class Filter;
+}
 
 // My testing shows that disabling cache reduces the amount 
 // of leaked memory when many documents are loaded one from the other,
@@ -71,6 +77,7 @@ struct NRGC {
 };
 
 struct NRArenaItem : public NRObject {
+
        NRArena *arena;
        Inkscape::GC::soft_ptr<NRArenaItem> parent;
        NRArenaItem *next;
@@ -102,12 +109,17 @@ struct NRArenaItem : public NRObject {
        NRArenaItem *clip;
        /* Mask item */
        NRArenaItem *mask;
+        /* Filter to be applied after rendering this object, NULL if none */
+        NR::Filter *filter;
        /* Rendered buffer */
        unsigned char *px;
 
        /* Single data member */
        void *data;
 
+        /* Current Transformation Matrix */
+        NR::Matrix ctm;
+
        void init(NRArena *arena) {
                this->arena = arena;
        }
index d26d1bc2792fb73c7b314dd70a9e3f732ffbaf84..abe343a1dac834e7107faccc7390b6ba121d3471 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <display/nr-arena.h>
 #include <display/nr-arena-shape.h>
+#include "display/nr-filter.h"
 #include <libnr/n-art-bpath.h>
 #include <libnr/nr-path.h>
 #include <libnr/nr-pixops.h>
@@ -26,6 +27,8 @@
 #include <livarot/float-line.h>
 #include <livarot/int-line.h>
 #include <style.h>
+/* prefs-utils used for deciding, whether to run filtering test or not */
+#include "prefs-utils.h"
 
 //int  showRuns=0;
 void nr_pixblock_render_shape_mask_or(NRPixBlock &m,Shape* theS);
@@ -1082,6 +1085,12 @@ nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style)
     }
     shape->setMitreLimit(style->stroke_miterlimit.value);
 
+    /* TODO: after SPStyle handles filters, get the correct filter
+     * from there. */
+    //if (prefs_get_double_attribute("options.filtertest", "value", 0) != 0)
+    if (style->filter.set && style->filter.filter)
+        shape->filter = new NR::Filter();
+
     nr_arena_item_request_update(shape, NR_ARENA_ITEM_STATE_ALL, FALSE);
 }
 
diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp
new file mode 100644 (file)
index 0000000..e31d903
--- /dev/null
@@ -0,0 +1,394 @@
+#define __NR_FILTER_GAUSSIAN_CPP__
+
+/*
+ * Gaussian blur renderer
+ *
+ * Author:
+ *   Niko Kiirala <niko@kiirala.com>
+ *
+ * Copyright (C) 2006 Niko Kiirala
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <cmath>
+
+using std::isnormal;
+
+#include "display/nr-filter-primitive.h"
+#include "display/nr-filter-gaussian.h"
+#include "libnr/nr-pixblock.h"
+#include "libnr/nr-matrix.h"
+#include "prefs-utils.h"
+
+namespace NR {
+
+FilterGaussian::FilterGaussian()
+{
+    _deviation_x = _deviation_y = prefs_get_double_attribute("options.filtertest", "value", 0.0);
+}
+
+FilterPrimitive *FilterGaussian::create()
+{
+    return new FilterGaussian();
+}
+
+int FilterGaussian::_kernel_size(Matrix const &trans)
+{
+    int length_x = _effect_area_scr_x(trans);
+    int length_y = _effect_area_scr_y(trans);
+    return _max(length_x, length_y) * 2 + 1;
+}
+
+void FilterGaussian::_make_kernel(double *kernel, double deviation, double expansion)
+{
+    double length = deviation * 3.0;
+    int scr_len = (int)std::floor(length * expansion);
+    if(scr_len < 1) scr_len = 1;
+    double d_sq = deviation * deviation * 2;
+    double step = length / scr_len;
+
+    double sum = 0;
+    for ( int i = 0; i < scr_len * 2 + 1 ; i++ ) {
+        double i_sq = (step * i - length) * (step * i - length);
+        sum += (std::exp(-i_sq / d_sq) / std::sqrt(M_PI * d_sq));
+    }
+
+    for ( int i = 0; i < scr_len * 2 + 1 ; i++ ) {
+        double i_sq = (step * i - length) * (step * i - length);
+        kernel[i] = (std::exp(-i_sq / d_sq) / std::sqrt(M_PI * d_sq)) / sum;
+    }
+}
+
+int FilterGaussian::_effect_area_scr_x(Matrix const &trans)
+{
+    int ret = (int)std::floor(_deviation_x * 3.0 * trans.expansionX());
+    if(ret < 1) ret = 1;
+    return ret;
+}
+
+int FilterGaussian::_effect_area_scr_y(Matrix const &trans)
+{
+    int ret = (int)std::floor(_deviation_y * 3.0 * trans.expansionY());
+    if(ret < 1) ret = 1;
+    return ret;
+}
+
+int FilterGaussian::_effect_subsample_step(int scr_len_x)
+{
+    if (scr_len_x < 16) {
+        return 1;
+    } else if (scr_len_x < 80) {
+        return 4;
+    } else if (scr_len_x < 160) {
+        return 8;
+    } else if (scr_len_x < 320) {
+        return 32;
+    } else if (scr_len_x < 640) {
+        return 64;
+    } else if (scr_len_x < 1280) {
+        return 256;
+    } else if (scr_len_x < 2560) {
+        return 1024; 
+    } else {
+        return 65536;
+    }
+}
+
+int FilterGaussian::_effect_subsample_step_log2(int scr_len_x)
+{
+    if (scr_len_x < 16) {
+        return 0;
+    } else if (scr_len_x < 80) {
+        return 2;
+    } else if (scr_len_x < 160) {
+        return 3;
+    } else if (scr_len_x < 320) {
+        return 5;
+    } else if (scr_len_x < 640) {
+        return 6;
+    } else if (scr_len_x < 1280) {
+        return 8;
+    } else if (scr_len_x < 2560) {
+        return 10; 
+    } else {
+        return 16;
+    }
+}
+
+
+int FilterGaussian::render(NRPixBlock **pb, Matrix const &trans)
+{
+    /* in holds the input pixblock */
+    NRPixBlock *in = pb[0];
+
+    /* Blur radius in screen units (pixels) */
+    int scr_len_x = _effect_area_scr_x(trans);
+    int scr_len_y = _effect_area_scr_y(trans);
+
+    // subsampling step; it depends on the radius, but somewhat nonlinearly, to make high zooms
+    // workable
+    int stepx = _effect_subsample_step(scr_len_x);
+    int stepx_l2 = _effect_subsample_step_log2(scr_len_x);
+    int stepy = _effect_subsample_step(scr_len_y);
+    int stepy_l2 = _effect_subsample_step_log2(scr_len_y);
+    int stepx2 = stepx >> 1;
+    int stepy2 = stepy >> 1;
+
+    /* buffer for x-axis blur */
+    NRPixBlock *bufx = new NRPixBlock;
+    /* buffer for y-axis blur */
+    NRPixBlock *bufy = new NRPixBlock;
+
+    // boundaries of the subsampled (smaller, unless step==1) buffers
+    int xd0 = (in->area.x0 >> stepx_l2);
+    int xd1 = (in->area.x1 >> stepx_l2) + 1;
+    int yd0 = (in->area.y0 >> stepy_l2);
+    int yd1 = (in->area.y1 >> stepy_l2) + 1;
+
+    // set up subsampled buffers
+    nr_pixblock_setup_fast(bufx, in->mode, xd0, yd0, xd1, yd1, true);
+    nr_pixblock_setup_fast(bufy, in->mode, xd0, yd0, xd1, yd1, true);
+
+    //mid->visible_area = in->visible_area;
+    //out->visible_area = in->visible_area;
+
+    /* Array for filter kernel, big enough to fit kernels for both X and Y
+     * direction kernel, one at time */
+    double kernel[_kernel_size(trans)];
+    
+    /* 1. Blur in direction of X-axis, from in to bufx (they have different resolution)*/
+    _make_kernel(kernel, _deviation_x, trans.expansionX());
+
+    for ( int y = bufx->area.y0 ; y < bufx->area.y1; y++ ) {
+
+        // corresponding line in the source buffer
+        int in_line;
+        if ((y << stepy_l2) >= in->area.y1) {
+            in_line = (in->area.y1 - in->area.y0 - 1) * in->rs;
+        } else {
+            in_line = ((y << stepy_l2) - (in->area.y0))  * in->rs;
+            if (in_line < 0)
+                in_line = 0;
+        }
+
+        // current line in bufx
+        int bufx_line = (y - yd0) * bufx->rs;
+
+        int skipbuf[4] = {INT_MIN, INT_MIN, INT_MIN, INT_MIN};
+
+        for ( int x = bufx->area.x0 ; x < bufx->area.x1 ; x++ ) {
+
+            // for all bytes of the pixel
+            for ( int byte = 0 ; byte < NR_PIXBLOCK_BPP(in) ; byte++) {
+
+                if(skipbuf[byte] > x) continue;
+
+                double sum = 0;
+                int last_in = -1;
+                int different_count = 0;
+
+                // go over our point's neighborhood on x axis in the in buffer, with stepx increment
+                for ( int i = -scr_len_x ; i <= scr_len_x ; i += stepx ) {
+
+                    // the pixel we're looking at
+                    int x_in = (((x << stepx_l2) + i + stepx2) >> stepx_l2) << stepx_l2;
+
+                    // distance from it to the current x,y
+                    int dist = x_in - (x << stepx_l2);
+                    if (dist < -scr_len_x) 
+                        dist = -scr_len_x;
+                    if (dist > scr_len_x) 
+                        dist = scr_len_x;
+
+                    if (x_in >= in->area.x1) {
+                        x_in = (in->area.x1 - in->area.x0 - 1);
+                    } else {
+                        x_in = (x_in - in->area.x0);
+                        if (x_in < 0)
+                            x_in = 0;
+                    }
+
+                    // value at the pixel
+                    unsigned char in_byte = NR_PIXBLOCK_PX(in)[in_line + NR_PIXBLOCK_BPP(in) * x_in + byte];
+
+                    // is it the same as last one we saw?
+                    if(in_byte != last_in) different_count++;
+                    last_in = in_byte;
+
+                    // sum pixels weighted by the kernel; multiply by stepx because we're skipping stepx pixels
+                    sum += stepx * in_byte * kernel[scr_len_x + dist];
+                }
+
+                // store the result in bufx
+                NR_PIXBLOCK_PX(bufx)[bufx_line + NR_PIXBLOCK_BPP(bufx) * (x - xd0) + byte] = (unsigned char)sum;
+
+                // optimization: if there was no variation within this point's neighborhood, 
+                // skip ahead while we keep seeing the same last_in byte: 
+                // blurring flat color would not change it anyway
+                if (different_count <= 1) {
+                    int pos = x + 1;
+                    while(((pos << stepx_l2) + scr_len_x) < in->area.x1 &&
+                          NR_PIXBLOCK_PX(in)[in_line + NR_PIXBLOCK_BPP(in) * ((pos << stepx_l2) + scr_len_x - in->area.x0) + byte] == last_in)
+                    {
+                        NR_PIXBLOCK_PX(bufx)[bufx_line + NR_PIXBLOCK_BPP(bufx) * (pos - xd0) + byte] = last_in;
+                        pos++;
+                    }
+                    skipbuf[byte] = pos;
+                }
+            }
+        }
+    }
+
+
+    /* 2. Blur in direction of Y-axis, from bufx to bufy (they have the same resolution) */
+    _make_kernel(kernel, _deviation_y, trans.expansionY());
+
+    for ( int x = bufy->area.x0 ; x < bufy->area.x1; x++ ) {
+
+        int bufy_disp = NR_PIXBLOCK_BPP(bufy) * (x - xd0);
+        int bufx_disp = NR_PIXBLOCK_BPP(bufx) * (x - xd0);
+
+        int skipbuf[4] = {INT_MIN, INT_MIN, INT_MIN, INT_MIN};
+
+        for ( int y = bufy->area.y0; y < bufy->area.y1; y++ ) {
+
+            int bufy_line = (y - yd0) * bufy->rs;
+
+            for ( int byte = 0 ; byte < NR_PIXBLOCK_BPP(bufx) ; byte++) {
+
+                if (skipbuf[byte] > y) continue;
+
+                double sum = 0;
+                int last_in = -1;
+                int different_count = 0;
+
+                for ( int i = -scr_len_y ; i <= scr_len_y ; i += stepy ) {
+
+                    int y_in = ((((y << stepy_l2) + i + stepy2) >> stepy_l2) - yd0);
+
+                    int dist = ((y_in + yd0) << stepy_l2) - (y << stepy_l2);
+                    if (dist < -scr_len_y) 
+                        dist = -scr_len_y;
+                    if (dist > scr_len_y) 
+                        dist = scr_len_y;
+
+                    if (y_in > (yd1 - yd0)) y_in = (yd1 - yd0);
+                    if (y_in < 0) y_in = 0;
+
+                    unsigned char in_byte = NR_PIXBLOCK_PX(bufx)[y_in * bufx->rs + NR_PIXBLOCK_BPP(bufx) * (x - xd0) + byte];
+                    if(in_byte != last_in) different_count++;
+                    last_in = in_byte;
+                    sum += stepy * in_byte * kernel[scr_len_y + dist];
+                }
+
+                NR_PIXBLOCK_PX(bufy)[bufy_line + bufy_disp + byte] = (unsigned char)sum;
+
+                if (different_count <= 1) {
+                    int pos = y + 1;
+                    while((pos + (scr_len_y >> stepy_l2) + 1) < yd1 &&
+                          NR_PIXBLOCK_PX(bufx)[(pos + (scr_len_y >> stepy_l2) + 1 - yd0) * bufx->rs + bufx_disp + byte] == last_in)
+                    {
+                        NR_PIXBLOCK_PX(bufy)[(pos - yd0) * bufy->rs + bufy_disp + byte] = last_in;
+                        pos++;
+                    }
+                    skipbuf[byte] = pos;
+                }
+
+            }
+        }
+    }
+
+    // we don't need bufx anymore
+    nr_pixblock_release(bufx);
+    delete bufx;
+
+    // interpolation will need to divide by stepx * stepy
+    int divisor = stepx_l2 + stepy_l2;
+
+    // new buffer for the final output, same resolution as the in buffer
+    NRPixBlock *out = new NRPixBlock;
+    nr_pixblock_setup_fast(out, in->mode, in->area.x0, in->area.y0,
+                           in->area.x1, in->area.y1, true);
+
+    for ( int y = yd0 ; y < yd1 - 1; y++ ) {
+        for ( int x = xd0 ; x < xd1 - 1; x++ ) {
+            for ( int byte = 0 ; byte < NR_PIXBLOCK_BPP(bufy) ; byte++) {
+
+                // get 4 values at the corners of the pixel from bufy
+                unsigned char a00 = NR_PIXBLOCK_PX(bufy)[((y - yd0) * bufy->rs) + NR_PIXBLOCK_BPP(bufy) * (x - xd0) + byte];
+                if (stepx == 1 && stepy == 1) { // if there was no subsampling, just use a00
+                    NR_PIXBLOCK_PX(out)[((y - yd0) * out->rs) + NR_PIXBLOCK_BPP(out) * (x - xd0) + byte] = a00;
+                    continue;
+                }
+                unsigned char a10 = NR_PIXBLOCK_PX(bufy)[((y - yd0) * bufy->rs) + NR_PIXBLOCK_BPP(bufy) * (x + 1 - xd0) + byte];
+                unsigned char a01 = NR_PIXBLOCK_PX(bufy)[((y + 1 - yd0) * bufy->rs) + NR_PIXBLOCK_BPP(bufy) * (x - xd0) + byte];
+                unsigned char a11 = NR_PIXBLOCK_PX(bufy)[((y + 1 - yd0) * bufy->rs) + NR_PIXBLOCK_BPP(bufy) * (x + 1 - xd0) + byte];
+
+                // iterate over the rectangle to be interpolated
+                for ( int yi = 0 ; yi < stepy; yi++ ) {
+                    int iy = stepy - yi;
+                    int y_out = (y << stepy_l2) + yi;
+                    if ((y_out < out->area.y0) || (y_out >= out->area.y1))
+                        continue;
+                    int out_line  = (y_out - out->area.y0) * out->rs;
+
+                    for ( int xi = 0 ; xi < stepx; xi++ ) {
+                        int ix = stepx - xi;
+                        int x_out = (x << stepx_l2) + xi;
+                        if ((x_out < out->area.x0) || (x_out >= out->area.x1))
+                            continue;
+
+                        // simple linear interpolation
+                        int a = (a00*ix*iy + a10*xi*iy + a01*ix*yi + a11*xi*yi) >> divisor;
+
+                        NR_PIXBLOCK_PX(out)[out_line + NR_PIXBLOCK_BPP(out) * (x_out - out->area.x0) + byte] = (unsigned char) a;
+                    }
+                }
+            }
+        }
+    }
+
+    nr_pixblock_release(bufy);
+    delete bufy;
+
+    out->empty = FALSE;
+    pb[1] = out;
+
+    return 0;
+}
+
+int FilterGaussian::get_enlarge(Matrix const &trans)
+{
+    int area_x = _effect_area_scr_x(trans);
+    int area_y = _effect_area_scr_y(trans);
+    return _max(area_x, area_y);
+}
+
+void FilterGaussian::set_deviation(double deviation)
+{
+    if(isnormal(deviation) && deviation >= 0) {
+        _deviation_x = _deviation_y = deviation;
+    }
+}
+
+void FilterGaussian::set_deviation(double x, double y)
+{
+    if(isnormal(x) && x >= 0 && isnormal(y) && y >= 0) {
+        _deviation_x = x;
+        _deviation_y = y;
+    }
+}
+
+} /* namespace NR */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/display/nr-filter-gaussian.h b/src/display/nr-filter-gaussian.h
new file mode 100644 (file)
index 0000000..445364a
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef __NR_FILTER_GAUSSIAN_H__
+#define __NR_FILTER_GAUSSIAN_H__
+
+/*
+ * Gaussian blur renderer
+ *
+ * Author:
+ *   Niko Kiirala <niko@kiirala.com>
+ *
+ * Copyright (C) 2006 Niko Kiirala
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "display/nr-filter-primitive.h"
+#include "libnr/nr-pixblock.h"
+#include "libnr/nr-matrix.h"
+
+namespace NR {
+
+class FilterGaussian : public FilterPrimitive {
+public:
+    FilterGaussian();
+    static FilterPrimitive *create();
+
+    virtual int render(NRPixBlock **pb, Matrix const &trans);
+    virtual int get_enlarge(Matrix const &m);
+
+    /**
+     * Set the standard deviation value for gaussian blur. Deviation along
+     * both axis is set to the provided value.
+     * Negative value, NaN and infinity are considered an error and no
+     * changes to filter state are made. If not set, default value of zero
+     * is used, which means the filter results in transparent black image.
+     */
+    void set_deviation(double deviation);
+    /**
+     * Set the standard deviation value for gaussian blur. First parameter
+     * sets the deviation alogn x-axis, second along y-axis.
+     * Negative value, NaN and infinity are considered an error and no
+     * changes to filter state are made. If not set, default value of zero
+     * is used, which means the filter results in transparent black image.
+     */
+    void set_deviation(double x, double y);
+
+private:
+    double _deviation_x;
+    double _deviation_y;
+
+    int _kernel_size(Matrix const &trans);
+    void _make_kernel(double *kernel, double deviation, double expansion);
+    int _effect_area_scr_x(Matrix const &trans);
+    int _effect_area_scr_y(Matrix const &trans);
+    int _effect_subsample_step(int scr_len_x);
+    int _effect_subsample_step_log2(int scr_len_x);
+
+    inline int _min(int const a, int const b)
+    {
+        return ((a < b) ? a : b);
+    }
+    inline int _max(int const a, int const b)
+    {
+        return ((a > b) ? a : b);
+    }
+};
+
+
+} /* namespace NR */
+
+
+
+
+#endif /* __NR_FILTER_GAUSSIAN_H__ */
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/display/nr-filter-primitive.cpp b/src/display/nr-filter-primitive.cpp
new file mode 100644 (file)
index 0000000..6bdbf9a
--- /dev/null
@@ -0,0 +1,70 @@
+#define __NR_FILTER_PRIMITIVE_CPP__
+
+/*
+ * SVG filters rendering
+ *
+ * Author:
+ *   Niko Kiirala <niko@kiirala.com>
+ *
+ * Copyright (C) 2006 Niko Kiirala
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "display/nr-filter-primitive.h"
+#include "display/nr-filter-types.h"
+#include "libnr/nr-pixblock.h"
+#include "svg/svg-length.h"
+
+namespace NR {
+
+FilterPrimitive::FilterPrimitive()
+{
+    _input = NR_FILTER_SLOT_NOT_SET;
+    _output = NR_FILTER_SLOT_NOT_SET;
+
+    _region_x.set(SVGLength::PERCENT, 0, 0);
+    _region_y.set(SVGLength::PERCENT, 0, 0);
+    _region_width.set(SVGLength::PERCENT, 100, 0);
+    _region_height.set(SVGLength::PERCENT, 100, 0);
+}
+
+int FilterPrimitive::render(NRPixBlock **pb, NRMatrix const *trans) {
+    if(trans) {
+        return this->render(pb, *trans);
+    } else {
+        Matrix tmp;
+        tmp.set_identity();
+        return this->render(pb, tmp);
+    }
+}
+
+int FilterPrimitive::get_enlarge(Matrix const &m)
+{
+    return 0;
+}
+
+void FilterPrimitive::set_input(int slot) {
+    set_input(0, slot);
+}
+
+void FilterPrimitive::set_input(int input, int slot) {
+    if (slot == 0) _input = slot;
+}
+
+void FilterPrimitive::set_output(int slot) {
+    if (slot >= 0) _output = slot;
+}
+
+} /* namespace NR */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/display/nr-filter-primitive.h b/src/display/nr-filter-primitive.h
new file mode 100644 (file)
index 0000000..a2bd14b
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef __NR_FILTER_PRIMITIVE_H__
+#define __NR_FILTER_PRIMITIVE_H__
+
+/*
+ * SVG filters rendering
+ *
+ * Author:
+ *   Niko Kiirala <niko@kiirala.com>
+ *
+ * Copyright (C) 2006 Niko Kiirala
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "libnr/nr-pixblock.h"
+#include "libnr/nr-matrix.h"
+#include "svg/svg-length.h"
+
+namespace NR {
+
+class FilterPrimitive {
+public:
+    FilterPrimitive();
+    int render(NRPixBlock **pb, NRMatrix const *trans);
+    virtual int render(NRPixBlock **pb, Matrix const &trans) = 0;
+    virtual int get_enlarge(Matrix const &m);
+
+    /**
+     * Sets the input slot number 'slot' to be used as input in rendering
+     * filter primitive 'primitive'
+     * For filter primitive types accepting more than one input, this sets the
+     * first input.
+     * If any of the required input slots is not set, the output of previous
+     * filter primitive is used, or SourceGraphic if this is the first
+     * primitive for this filter.
+     */
+    virtual void set_input(int slot);
+
+    /**
+     * Sets the input slot number 'slot' to be user as input number 'input' in
+     * rendering filter primitive 'primitive'
+     * First input for a filter primitive is number 0. For primitives with
+     * attributes 'in' and 'in2', these are numbered 0 and 1, respectively.
+     * If any of required input slots for a filter is not set, the output of
+     * previous filter primitive is used, or SourceGraphic if this is the first
+     * filter primitive for this filter.
+     */
+    virtual void set_input(int input, int slot);
+
+    /**
+     * Sets the slot number 'slot' to be used as output from filter primitive
+     * 'primitive'
+     * If output slot for a filter element is not set, one of the unused image
+     * slots is used.
+     * It is an error to specify a pre-defined slot as 'slot'. Such call does
+     * not have any effect to the state of filter or its primitives.
+     */
+    virtual void set_output(int slot);
+
+    void set_x(SVGLength &length);
+    void set_y(SVGLength &length);
+    void set_width(SVGLength &length);
+    void set_height(SVGLength &length);
+
+    /**
+     * Sets the filter primitive subregion. Passing an unset length
+     * (length._set == false) as any parameter results in that parameter
+     * not being changed.
+     * Filter primitive will not hold any references to the passed
+     * SVGLength object after function returns.
+     * If any of the parameters does not get set the default value, as
+     * defined in SVG standard, for that parameter is used instead.
+     */
+    void set_region(SVGLength &x, SVGLength &y,
+                    SVGLength &width, SVGLength &height);
+
+    /**
+     * Resets the filter primitive subregion to its default value
+     */
+    void reset_region();
+
+protected:
+    int _input;
+    int _output;
+
+    SVGLength _region_x;
+    SVGLength _region_y;
+    SVGLength _region_width;
+    SVGLength _region_height;
+};
+
+
+} /* namespace NR */
+
+
+
+
+#endif /* __NR_FILTER_PRIMITIVE_H__ */
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/display/nr-filter-types.h b/src/display/nr-filter-types.h
new file mode 100644 (file)
index 0000000..a6f539d
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef __NR_FILTER_TYPES_H__
+#define __NR_FILTER_TYPES_H__
+
+namespace NR {
+
+enum FilterPrimitiveType {
+    NR_FILTER_BLEND,
+    NR_FILTER_COLORMATRIX,
+    NR_FILTER_COMPONENTTRANSFER,
+    NR_FILTER_COMPOSITE,
+    NR_FILTER_CONVOLVEMATRIX,
+    NR_FILTER_DIFFUSELIGHTING,
+    NR_FILTER_DISPLACEMENTMAP,
+    NR_FILTER_FLOOD,
+    NR_FILTER_GAUSSIANBLUR,
+    NR_FILTER_IMAGE,
+    NR_FILTER_MERGE,
+    NR_FILTER_MORPHOLOGY,
+    NR_FILTER_OFFSET,
+    NR_FILTER_SPECULARLIGHTING,
+    NR_FILTER_TILE,
+    NR_FILTER_TURBULENCE,
+    NR_FILTER_ENDPRIMITIVETYPE // This must be last
+};
+//const int Filter::_filter_primitive_type_count = 16;
+
+enum {
+    NR_FILTER_SLOT_NOT_SET = -1,
+    NR_FILTER_SOURCEGRAPHIC = -2,
+    NR_FILTER_SOURCEALPHA = -3,
+    NR_FILTER_BACKGROUNDIMAGE = -4,
+    NR_FILTER_BACKGROUNDAPLHA = -5,
+    NR_FILTER_FILLPAINT = -6,
+    NR_FILTER_SOURCEPAINT = -7
+};
+
+} /* namespace NR */
+
+#endif // __NR_FILTER_TYPES_H__
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp
new file mode 100644 (file)
index 0000000..35f3483
--- /dev/null
@@ -0,0 +1,223 @@
+#define __NR_FILTER_CPP__
+
+/*
+ * SVG filters rendering
+ *
+ * Author:
+ *   Niko Kiirala <niko@kiirala.com>
+ *
+ * Copyright (C) 2006 Niko Kiirala
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "display/nr-filter.h"
+#include "display/nr-filter-primitive.h"
+#include "display/nr-filter-gaussian.h"
+
+#include "display/nr-arena-item.h"
+#include "libnr/nr-pixblock.h"
+#include "libnr/nr-blit.h"
+#include "svg/svg-length.h"
+#include "sp-filter-units.h"
+
+//#include "display/nr-arena-shape.h"
+
+namespace NR {
+
+Filter::Filter()
+{
+    _primitive_count = 1;
+    _primitive_table_size = 1;
+    _primitive = new FilterPrimitive*[1];
+    _primitive[0] = new FilterGaussian;
+    _common_init();
+}
+
+Filter::Filter(int n)
+{
+    _primitive_count = 0;
+    _primitive_table_size = n;
+    _primitive = new FilterPrimitive*[n];
+    for ( int i = 0 ; i < n ; i++ ) {
+        _primitive[i] = NULL;
+    }
+    _common_init();
+}
+
+void Filter::_common_init() {
+    _slot_count = 1;
+    _output_slot = -1;
+
+    _region_x.set(SVGLength::PERCENT, -10, 0);
+    _region_y.set(SVGLength::PERCENT, -10, 0);
+    _region_width.set(SVGLength::PERCENT, 120, 0);
+    _region_height.set(SVGLength::PERCENT, 120, 0);
+
+    _x_pixels = -1.0;
+    _y_pixels = -1.0;
+
+    _filter_units = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+    _primitive_units = SP_FILTER_UNITS_USERSPACEONUSE;
+}
+
+Filter::~Filter()
+{
+    clear_primitives();
+    delete[] _primitive;
+}
+
+
+int Filter::render(NRArenaItem const *item, NRPixBlock *pb)
+{
+    NRPixBlock *slot[2];
+    slot[0] = pb;
+    slot[1] = NULL;
+
+    _primitive[0]->render(slot, *item->ctm);
+
+
+    int size = (slot[0]->area.x1 - slot[0]->area.x0)
+        * (slot[0]->area.y1 - slot[0]->area.y0)
+        * NR_PIXBLOCK_BPP(slot[0]);
+    memset(NR_PIXBLOCK_PX(slot[0]), 0, size);
+
+    nr_blit_pixblock_pixblock(slot[0], slot[1]);
+
+    slot[0]->visible_area = slot[0]->area;
+
+    nr_pixblock_release(slot[1]);
+
+    return 0;
+}
+
+int Filter::get_enlarge(Matrix const &m)
+{
+    int enlarge = 0;
+    for ( int i = 0 ; i < _primitive_count ; i++ ) {
+        if(_primitive[i]) enlarge += _primitive[i]->get_enlarge(m);
+    }
+    return enlarge;
+}
+
+void Filter::bbox_enlarge(NRRectL &bbox)
+{
+    int len_x = bbox.x1 - bbox.x0;
+    int len_y = bbox.y1 - bbox.y0;
+    int enlarge_x = (int)std::ceil(len_x / 10.0);
+    int enlarge_y = (int)std::ceil(len_y / 10.0);
+    bbox.x0 -= enlarge_x;
+    bbox.x1 += enlarge_x;
+    bbox.y0 -= enlarge_y;
+    bbox.y1 += enlarge_y;
+}
+
+typedef FilterPrimitive*(*FilterConstructor)();
+static FilterConstructor _constructor[NR_FILTER_ENDPRIMITIVETYPE];
+
+void Filter::_create_constructor_table()
+{
+    static bool created = false;
+    if(created) return;
+
+    /* Filter effects not yet implemented are set to NULL */
+    _constructor[NR_FILTER_BLEND] = NULL;
+    _constructor[NR_FILTER_COLORMATRIX] = NULL;
+    _constructor[NR_FILTER_COMPONENTTRANSFER] = NULL;
+    _constructor[NR_FILTER_COMPOSITE] = NULL;
+    _constructor[NR_FILTER_CONVOLVEMATRIX] = NULL;
+    _constructor[NR_FILTER_DIFFUSELIGHTING] = NULL;
+    _constructor[NR_FILTER_DISPLACEMENTMAP] = NULL;
+    _constructor[NR_FILTER_FLOOD] = NULL;
+    _constructor[NR_FILTER_GAUSSIANBLUR] = &FilterGaussian::create;
+    _constructor[NR_FILTER_IMAGE] = NULL;
+    _constructor[NR_FILTER_MERGE] = NULL;
+    _constructor[NR_FILTER_MORPHOLOGY] = NULL;
+    _constructor[NR_FILTER_OFFSET] = NULL;
+    _constructor[NR_FILTER_SPECULARLIGHTING] = NULL;
+    _constructor[NR_FILTER_TILE] = NULL;
+    _constructor[NR_FILTER_TURBULENCE] = NULL;
+}
+
+void Filter::_enlarge_primitive_table() {
+    FilterPrimitive **new_tbl = new FilterPrimitive*[_primitive_table_size * 2];
+    for (int i = 0 ; i < _primitive_count ; i++) {
+        new_tbl[i] = _primitive[i];
+    }
+    _primitive_table_size *= 2;
+    for (int i = _primitive_count ; i < _primitive_table_size ; i++) {
+        new_tbl[i] = NULL;
+    }
+    delete[] _primitive;
+    _primitive = new_tbl;
+}
+
+FilterPrimitive *Filter::add_primitive(FilterPrimitiveType type)
+{
+    _create_constructor_table();
+
+    // Check that we can create a new filter of specified type
+    if (type < 0 || type >= NR_FILTER_ENDPRIMITIVETYPE)
+        return NULL;
+    if (!_constructor[type]) return NULL;
+    FilterPrimitive *created = _constructor[type]();
+
+    // If there is no space for new filter primitive, enlarge the table
+    if (_primitive_count >= _primitive_table_size) {
+        _enlarge_primitive_table();
+    }
+
+    _primitive[_primitive_count] = created;
+    return created;
+}
+
+FilterPrimitive *Filter::replace_primitive(FilterPrimitive *target, FilterPrimitiveType type)
+{
+    _create_constructor_table();
+
+    // Check that target is valid primitive inside this filter
+    int place = -1;
+    for (int i = 0 ; i < _primitive_count ; i++) {
+        if (target == _primitive[i]) {
+            place = i;
+            break;
+        }
+    }
+    if (place < 0) return NULL;
+
+    // Check that we can create a new filter of specified type
+    if (type < 0 || type >= NR_FILTER_ENDPRIMITIVETYPE)
+        return NULL;
+    if (!_constructor[type]) return NULL;
+    FilterPrimitive *created = _constructor[type]();
+
+    // If there is no space for new filter primitive, enlarge the table
+    if (_primitive_count >= _primitive_table_size) {
+        _enlarge_primitive_table();
+    }
+
+    delete target;
+    _primitive[place] = created;
+    return created;
+}
+
+void Filter::clear_primitives()
+{
+    for (int i = 0 ; i < _primitive_count ; i++) {
+        if (_primitive[i]) delete _primitive[i];
+    }
+    _primitive_count = 0;
+}
+
+} /* namespace NR */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/display/nr-filter.h b/src/display/nr-filter.h
new file mode 100644 (file)
index 0000000..15a852d
--- /dev/null
@@ -0,0 +1,204 @@
+#ifndef __NR_FILTER_H__
+#define __NR_FILTER_H__
+
+/*
+ * SVG filters rendering
+ *
+ * Author:
+ *   Niko Kiirala <niko@kiirala.com>
+ *
+ * Copyright (C) 2006 Niko Kiirala
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "display/nr-arena-item.h"
+#include "display/nr-filter-primitive.h"
+#include "display/nr-filter-types.h"
+#include "libnr/nr-pixblock.h"
+#include "libnr/nr-matrix.h"
+#include "libnr/nr-rect.h"
+#include "svg/svg-length.h"
+#include "sp-filter-units.h"
+#include "gc-managed.h"
+
+namespace NR {
+
+class Filter : public Inkscape::GC::Managed<> {
+public:
+    int render(NRArenaItem const *item, NRPixBlock *pb);
+
+    /**
+     * Creates a new filter primitive under this filter object.
+     * New primitive is placed so that it will be executed after all filter
+     * primitives defined beforehand for this filter object.
+     * Should this filter not have enough space for a new primitive, the filter
+     * is enlarged to accomodate the new filter element. It may be enlarged by
+     * more that one element.
+     * Returns a pointer to the filter primitive created.
+     * Returns NULL, if type is not valid filter primitive type or filter
+     * primitive of such type cannot be created.
+     */
+    FilterPrimitive *add_primitive(FilterPrimitiveType type);
+    /**
+     * Removes all filter primitives from this filter.
+     * All pointers to filter primitives inside this filter should be
+     * considered invalid after calling this function.
+     */
+    void clear_primitives();
+    /**
+     * Replaces filter primitive pointed by 'target' with a new filter
+     * primitive of type 'type'
+     * If 'target' does not correspond to any primitive inside this filter OR
+     * 'type' is not a valid filter primitive type OR
+     * filter primitive of such type cannot be created,
+     * this function returns NULL and doesn't change the internal state of this
+     * filter.
+     * Otherwise, a new filter primitive is created. Any pointers to filter
+     * primitive 'target' should be considered invalid. A pointer to the
+     * newly created primitive is returned.
+     */
+    FilterPrimitive *replace_primitive(FilterPrimitive *primitive,
+                                       FilterPrimitiveType type);
+
+    /**
+     * Sets the slot number 'slot' to be used as result from this filter.
+     * If output is not set, the output from last filter primitive is used as
+     * output from the filter.
+     * It is an error to specify a pre-defined slot as 'slot'. Such call does
+     * not have any effect to the state of filter or its primitives.
+     */
+    void set_output(int slot);
+
+    void set_x(SVGLength &lenght);
+    void set_y(SVGLength &length);
+    void set_width(SVGLength &length);
+    void set_height(SVGLength &length);
+
+    /**
+     * Sets the filter effects region.
+     * Passing an unset length (length._set == false) as any of the parameters
+     * results in that parameter not being changed.
+     * Filter will not hold any references to the passed SVGLength object after
+     * function returns.
+     * If any of these parameters does not get set, the default value, as
+     * defined in SVG standard, for that parameter is used instead.
+     */
+    void set_region(SVGLength &x, SVGLength &y,
+                    SVGLength &width, SVGLength &height);
+
+    /**
+     * Resets the filter effects region to its default value as defined
+     * in SVG standard.
+     */
+    void reset_region();
+
+    /**
+     * Sets the width of intermediate images in pixels. If not set, suitable
+     * resolution is determined automatically. If x_pixels is less than zero,
+     * calling this function results in no changes to filter state.
+     */
+    void set_resolution(double x_pixels);
+
+    /**
+     * Sets the width and height of intermediate images in pixels. If not set,
+     * suitable resolution is determined automatically. If either parameter is
+     * less than zero, calling this function results in no changes to filter
+     * state.
+     */
+    void set_resolution(double x_pixels, double y_pixels);
+
+    /**
+     * Resets the filter resolution to its default value, i.e. automatically
+     * determined.
+     */
+    void reset_resolution();
+
+    /**
+     * Set the filterUnits-property. If not set, the default value of 
+     * objectBoundingBox is used. If the parameter value is not a
+     * valid enumeration value from SPFilterUnits, no changes to filter state
+     * are made.
+     */
+    void set_filter_units(SPFilterUnits unit);
+
+    /**
+     * Set the primitiveUnits-properterty. If not set, the default value of
+     * userSpaceOnUseis used. If the parameter value is not a valid
+     * enumeration value from SPFilterUnits, no changes to filter state
+     * are made.
+     */
+    void set_primitive_units(SPFilterUnits unit);
+
+    /** 
+     * Returns the amount of pixels the rendering area should be enlarged
+     * to prevent visual artefacts when filter needs to read pixels that
+     * are outside its output area (e.g. gaussian blur)
+     */
+    int get_enlarge(Matrix const &m);
+    /**
+     * Given an object bounding box, this function enlarges it so that
+     * it contains the filter effect area.
+     */
+    void bbox_enlarge(NRRectL &bbox);
+
+    /** Creates a new filter with space for one filter element */
+    Filter();
+    /** 
+     * Creates a new filter with space for n filter elements. If number of
+     * filter elements is known beforehand, it's better to use this
+     * constructor.
+     */
+    Filter(int n);
+    /** Destroys the filter and all its primitives */
+    ~Filter();
+
+private:
+    int _primitive_count;
+    int _primitive_table_size;
+
+    /** Amount of image slots used, when this filter was rendered last time */
+    int _slot_count;
+
+    /** Image slot, from which filter output should be read.
+     * Negative values mean 'not set' */
+    int _output_slot;
+
+    SVGLength _region_x;
+    SVGLength _region_y;
+    SVGLength _region_width;
+    SVGLength _region_height;
+
+    /* x- and y-resolutions for filter rendering.
+     * Negative values mean 'not set'.
+     * If _y_pixels is set, _x_pixels should be set, too. */
+    double _x_pixels;
+    double _y_pixels;
+
+    SPFilterUnits _filter_units;
+    SPFilterUnits _primitive_units;
+
+    FilterPrimitive ** _primitive;
+
+    void _create_constructor_table();
+    void _enlarge_primitive_table();
+    void _common_init();
+};
+
+
+} /* namespace NR */
+
+
+
+
+#endif /* __NR_FILTER_H__ */
+/*
+  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:encoding=utf-8:textwidth=99 :
index 0a8287bdea1b77b73cd83122d9639b0ae6bde8f8..6d881e7b0a75fac20b9740d84bb8862c2b521d8c 100644 (file)
@@ -150,6 +150,17 @@ nr_rect_d_matrix_transform(NRRect *d, NRRect const *s, NRMatrix const *m)
     return nr_rect_d_matrix_transform(d, s, *m);
 }
 
+/** Enlarges the rectangle given amount of pixels to all directions */
+NRRectL *
+nr_rect_l_enlarge(NRRectL *d, int amount)
+{
+    d->x0 -= amount;
+    d->y0 -= amount;
+    d->x1 += amount;
+    d->y1 += amount;
+    return d;
+}
+
 namespace NR {
 
 Rect::Rect(const Point &p0, const Point &p1)
index 767eab90243fdde17bc77723453f1506a3ba46d8..ab78c1651c53372eb9005d55520ba323c5929c58 100644 (file)
@@ -69,6 +69,7 @@ NRRectL *nr_rect_l_union_xy(NRRectL *d, NR::ICoord x, NR::ICoord y);
 
 NRRect *nr_rect_d_matrix_transform(NRRect *d, NRRect const *s, NR::Matrix const &m);
 NRRect *nr_rect_d_matrix_transform(NRRect *d, NRRect const *s, NRMatrix const *m);
+NRRectL *nr_rect_l_enlarge(NRRectL *d, int amount);
 
 namespace NR {
 
diff --git a/src/number-opt-number.h b/src/number-opt-number.h
new file mode 100644 (file)
index 0000000..c08de58
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef SEEN_NUMBER_OPT_NUMBER_H
+#define SEEN_NUMBER_OPT_NUMBER_H
+
+/** \file
+ * <number-opt-number> implementation.
+ */
+/*
+ * Authors:
+ *   Hugo Rodrigues <haa.rodrigues@gmail.com>
+ *
+ * Copyright (C) 2006 Hugo Rodrigues
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+#include <glib/gprintf.h>
+//todo: use glib instead of stdlib
+#include <stdlib.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+class NumberOptNumber {
+
+public:
+
+    gfloat number; 
+
+    gfloat optNumber;
+
+    guint _set : 1;
+
+    guint optNumber_set : 1;
+
+    NumberOptNumber()
+    {
+        number = 0.0;
+        optNumber = 0.0;
+
+        _set = FALSE;
+        optNumber_set = FALSE;
+    }
+
+    gfloat getNumber()
+    { return number; }
+
+    gfloat getOptNumber()
+    { return optNumber; }
+
+    gchar *getValueString(gchar *str)
+    { 
+        if( _set )
+        {
+
+            if( optNumber_set )
+            {
+                g_sprintf(str, "%lf %lf", number, optNumber);
+            }
+            else {
+                g_sprintf(str, "%lf", number);
+            }
+        }
+        return str;
+    }
+
+    void set(gchar const *str)
+    {
+        if(!str)
+            return;
+
+        gchar **values = g_strsplit(str, " ", 2);
+
+        if( values[0] != NULL )
+        {
+            number = strtof(values[0], NULL);
+            _set = TRUE;
+
+            if( values[1] != NULL )
+            {
+  //              optNumber = g_ascii_strtod(values[1], NULL);
+                optNumber = strtof(values[1], NULL);
+                optNumber_set = TRUE;
+            }
+            else
+                optNumber_set = FALSE;
+        }
+        else {
+                _set = FALSE;
+                optNumber_set = FALSE;
+        }
+    }
+
+};
+
+#endif /* !SEEN_NUMBER_OPT_NUMBER_H */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/sp-filter-fns.h b/src/sp-filter-fns.h
new file mode 100644 (file)
index 0000000..21913c9
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef SEEN_SP_FILTER_FNS_H
+#define SEEN_SP_FILTER_FNS_H
+
+/** \file
+ * Macros and fn declarations related to filters.
+ */
+
+#include <glib/gtypes.h>
+#include <glib-object.h>
+#include "libnr/nr-forward.h"
+#include "sp-filter-units.h"
+
+class SPFilter;
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+}
+
+#define SP_TYPE_FILTER (sp_filter_get_type())
+#define SP_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_FILTER, SPFilter))
+#define SP_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_FILTER, SPFilterClass))
+#define SP_IS_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_FILTER))
+#define SP_IS_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_FILTER))
+
+#define SP_FILTER_FILTER_UNITS(f) (SP_FILTER(f)->filterUnits)
+#define SP_FILTER_PRIMITIVE_UNITS(f) (SP_FILTER(f)->primitiveUnits)
+
+GType sp_filter_get_type();
+
+//need to define function
+void sp_filter_set_filter_units(SPFilter *filter, SPFilterUnits filterUnits);
+//need to define function
+void sp_filter_set_primitive_units(SPFilter *filter, SPFilterUnits filterUnits);
+
+
+#endif /* !SEEN_SP_FILTER_FNS_H */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/sp-filter-reference.cpp b/src/sp-filter-reference.cpp
new file mode 100644 (file)
index 0000000..18e1876
--- /dev/null
@@ -0,0 +1,21 @@
+#include "sp-filter-reference.h"
+
+bool
+SPFilterReference::_acceptObject(SPObject *obj) const
+{
+    return SP_IS_FILTER(obj);
+    /* effic: Don't bother making this an inline function: _acceptObject is a virtual function,
+       typically called from a context where the runtime type is not known at compile time. */
+}
+
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/sp-filter-reference.h b/src/sp-filter-reference.h
new file mode 100644 (file)
index 0000000..226e033
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef SEEN_SP_FILTER_REFERENCE_H
+#define SEEN_SP_FILTER_REFERENCE_H
+
+#include "uri-references.h"
+#include "sp-filter-fns.h"
+class SPObject;
+
+class SPFilterReference : public Inkscape::URIReference {
+public:
+    SPFilterReference(SPObject *obj) : URIReference(obj) {}
+
+    SPFilter *getObject() const {
+        return (SPFilter *)URIReference::getObject();
+    }
+
+protected:
+    virtual bool _acceptObject(SPObject *obj) const;
+};
+
+
+#endif /* !SEEN_SP_FILTER_REFERENCE_H */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/sp-filter-units.h b/src/sp-filter-units.h
new file mode 100644 (file)
index 0000000..1b6e7b7
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef SEEN_SP_FILTER_UNITS_H
+#define SEEN_SP_FILTER_UNITS_H
+
+enum SPFilterUnits {
+    SP_FILTER_UNITS_OBJECTBOUNDINGBOX,
+    SP_FILTER_UNITS_USERSPACEONUSE
+};
+
+
+#endif /* !SEEN_SP_GRADIENT_UNITS_H */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/sp-filter.cpp b/src/sp-filter.cpp
new file mode 100644 (file)
index 0000000..c3a4389
--- /dev/null
@@ -0,0 +1,424 @@
+#define __SP_FILTER_CPP__
+
+/** \file
+ * SVG <filter> implementation.
+ */
+/*
+ * Authors:
+ *   Hugo Rodrigues <haa.rodrigues@gmail.com>
+ *
+ * Copyright (C) 2006 Hugo Rodrigues
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "attributes.h"
+#include "document.h"
+#include "sp-filter.h"
+#include "sp-filter-reference.h"
+#include "uri.h"
+#include "xml/repr.h"
+
+#define SP_MACROS_SILENT
+#include "macros.h"
+
+#define DEBUG_FILTER
+#ifdef DEBUG_FILTER
+# define debug(f, a...) { g_print("%s(%d) %s:", \
+                                  __FILE__,__LINE__,__FUNCTION__); \
+                          g_print(f, ## a); \
+                          g_print("\n"); \
+                        }
+#else
+# define debug(f, a...) /**/
+#endif
+
+
+/*
+ * 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());
+
+}
+
+
+
+/* Filter base class */
+
+static void sp_filter_class_init(SPFilterClass *klass);
+static void sp_filter_init(SPFilter *filter);
+
+static void sp_filter_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
+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 filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter);
+static void filter_ref_modified(SPObject *href, SPFilter *filter);
+
+static SPObjectClass *filter_parent_class;
+
+GType
+sp_filter_get_type()
+{
+    static GType filter_type = 0;
+
+    if (!filter_type) {
+        GTypeInfo filter_info = {
+            sizeof(SPFilterClass),
+            NULL, NULL,
+            (GClassInitFunc) sp_filter_class_init,
+            NULL, NULL,
+            sizeof(SPFilter),
+            16,
+            (GInstanceInitFunc) sp_filter_init,
+            NULL,    /* value_table */
+        };
+        filter_type = g_type_register_static(SP_TYPE_OBJECT, "SPFilter", &filter_info, (GTypeFlags)0);
+    }
+    return filter_type;
+}
+
+static void
+sp_filter_class_init(SPFilterClass *klass)
+{
+
+    SPObjectClass *sp_object_class = (SPObjectClass *)klass;
+
+    filter_parent_class = (SPObjectClass*)g_type_class_peek_parent(klass);
+
+    sp_object_class->build = sp_filter_build;
+    sp_object_class->release = sp_filter_release;
+    sp_object_class->write = sp_filter_write;
+    sp_object_class->set = sp_filter_set;
+    sp_object_class->update = sp_filter_update;
+}
+
+static void
+sp_filter_init(SPFilter *filter)
+{
+    filter->href = new SPFilterReference(SP_OBJECT(filter));
+    filter->href->changedSignal().connect(sigc::bind(sigc::ptr_fun(filter_ref_changed), filter));
+
+    filter->x = 0;
+    filter->y = 0;
+    filter->width = 0;
+    filter->height = 0;
+
+    filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+    filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+    filter->filterUnits_set = FALSE;
+    filter->primitiveUnits_set = FALSE;
+
+}
+
+/**
+ * Reads the Inkscape::XML::Node, and initializes SPFilter variables.  For this to get called,
+ * our name must be associated with a repr via "sp_object_type_register".  Best done through
+ * sp-object-repr.cpp's repr_name_entries array.
+ */
+static void
+sp_filter_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+    debug("0x%p",object);
+    if (((SPObjectClass *) filter_parent_class)->build) {
+        ((SPObjectClass *) filter_parent_class)->build(object, document, repr);
+    }
+
+    //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");
+
+//is this necessary?
+    sp_document_add_resource(document, "filter", object);
+}
+
+/**
+ * Drops any allocated memory.
+ */
+static void
+sp_filter_release(SPObject *object)
+{
+    debug("0x%p",object);
+    SPFilter *filter = SP_FILTER(object);
+
+    if (SP_OBJECT_DOCUMENT(object)) {
+        /* Unregister ourselves */
+        sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "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->href->detach();
+        delete filter->href;
+        filter->href = NULL;
+    }
+
+    if (((SPObjectClass *) filter_parent_class)->release)
+        ((SPObjectClass *) filter_parent_class)->release(object);
+}
+
+/**
+ * Sets a specific value in the SPFilter.
+ */
+static void
+sp_filter_set(SPObject *object, unsigned int key, gchar const *value)
+{
+    debug("0x%p %s(%u): '%s'",object,
+            sp_attribute_name(key),key,value);
+    SPFilter *filter = SP_FILTER(object);
+
+    switch (key) {
+        case SP_ATTR_FILTERUNITS:
+            if (value) {
+                if (!strcmp(value, "userSpaceOnUse")) {
+                    filter->filterUnits = SP_FILTER_UNITS_USERSPACEONUSE;
+                } else {
+                    filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+                }
+                filter->filterUnits_set = TRUE;
+            } else {
+                filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+                filter->filterUnits_set = FALSE;
+            }
+            object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+            break;
+        case SP_ATTR_PRIMITIVEUNITS:
+            if (value) {
+                if (!strcmp(value, "userSpaceOnUse")) {
+                    filter->primitiveUnits = SP_FILTER_UNITS_USERSPACEONUSE;
+                } else {
+                    filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+                }
+                filter->primitiveUnits_set = TRUE;
+            } else {
+                filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
+                filter->primitiveUnits_set = FALSE;
+            }
+            object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+            break;
+       case SP_ATTR_X:
+            filter->x.readOrUnset(value);
+           object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+            break;
+       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);
+            break;
+       case SP_ATTR_HEIGHT:
+           filter->height.readOrUnset(value);
+           object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+            break;
+       case SP_ATTR_FILTERRES:
+               filter->filterRes.set(value);
+            break;
+        case SP_ATTR_XLINK_HREF:
+            if (value) {
+                try {
+                    filter->href->attach(Inkscape::URI(value));
+                } catch (Inkscape::BadURIException &e) {
+                    g_warning("%s", e.what());
+                    filter->href->detach();
+                }
+            } else {
+                filter->href->detach();
+            }
+            break;
+        default:
+            /* See if any parents need this value. */
+            if (((SPObjectClass *) filter_parent_class)->set) {
+                ((SPObjectClass *) filter_parent_class)->set(object, key, value);
+            }
+            break;
+    }
+}
+
+/**
+ * Receives update notifications.
+ */
+static void
+sp_filter_update(SPObject *object, SPCtx *ctx, guint flags)
+{
+    debug("0x%p",object);
+    //SPFilter *filter = SP_FILTER(object);
+
+    if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
+                 SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
+
+        /* do something to trigger redisplay, updates? */
+
+    }
+
+    if (((SPObjectClass *) filter_parent_class)->update) {
+        ((SPObjectClass *) filter_parent_class)->update(object, ctx, 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)
+{
+    debug("0x%p",object);
+    SPFilter *filter = SP_FILTER(object);
+
+    // Inkscape-only object, not copied during an "plain SVG" dump:
+/*    if (flags & SP_OBJECT_WRITE_EXT) {
+        if (repr) {
+            // is this sane?
+            repr->mergeFrom(SP_OBJECT_REPR(object), "id");
+        } else {
+            repr = SP_OBJECT_REPR(object)->duplicate();
+        }
+    }
+*/
+
+
+//FIXME: repr node is null at this point,
+//       preventing plain svg save. Need to fix this.
+
+
+    if ((flags & SP_OBJECT_WRITE_ALL) || filter->filterUnits_set) {
+        switch (filter->filterUnits) {
+            case SP_FILTER_UNITS_USERSPACEONUSE:
+                repr->setAttribute("filterUnits", "userSpaceOnUse");
+                break;
+            default:
+                repr->setAttribute("filterUnits", "objectBoundingBox");
+                break;
+        }
+    }
+
+    if ((flags & SP_OBJECT_WRITE_ALL) || filter->primitiveUnits_set) {
+        switch (filter->primitiveUnits) {
+            case SP_FILTER_UNITS_USERSPACEONUSE:
+                repr->setAttribute("primitiveUnits", "userSpaceOnUse");
+                break;
+            default:
+                repr->setAttribute("primitiveUnits", "objectBoundingBox");
+                break;
+        }
+    }
+
+    if (filter->x._set) {
+        sp_repr_set_svg_double(repr, "x", filter->x.computed);
+    } else {
+        repr->setAttribute("x", NULL);
+    }
+
+    if (filter->y._set) {
+        sp_repr_set_svg_double(repr, "y", filter->y.computed);
+    } else {
+        repr->setAttribute("y", NULL);
+    }
+
+    if (filter->width._set) {
+        sp_repr_set_svg_double(repr, "width", filter->width.computed);
+    } else {
+        repr->setAttribute("width", NULL);
+    }
+
+    if (filter->height._set) {
+        sp_repr_set_svg_double(repr, "height", filter->height.computed);
+    } else {
+        repr->setAttribute("height", NULL);
+    }
+
+    if (filter->filterRes._set) {
+       char filterRes[32];
+        repr->setAttribute("filterRes", filter->filterRes.getValueString(filterRes));
+    } else {
+        repr->setAttribute("filterRes", NULL);
+    }
+
+    if (filter->href->getURI()) {
+        gchar *uri_string = filter->href->getURI()->toString();
+        repr->setAttribute("xlink:href", uri_string);
+        g_free(uri_string);
+    }
+
+    if (((SPObjectClass *) filter_parent_class)->write) {
+        ((SPObjectClass *) filter_parent_class)->write(object, repr, flags);
+    }
+
+    return repr;
+}
+
+
+/**
+ * Gets called when the filter is (re)attached to another filter.
+ */
+static void
+filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter)
+{
+    if (old_ref) {
+        sp_signal_disconnect_by_data(old_ref, filter);
+    }
+    if ( SP_IS_FILTER(ref)
+         && ref != filter )
+    {
+        g_signal_connect(G_OBJECT(ref), "modified", G_CALLBACK(filter_ref_modified), filter);
+    }
+
+    filter_ref_modified(ref, filter);
+}
+
+static void
+filter_ref_modified(SPObject *href, SPFilter *filter)
+{
+    SP_OBJECT(filter)->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/sp-filter.h b/src/sp-filter.h
new file mode 100644 (file)
index 0000000..74cb1bf
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef SP_FILTER_H_SEEN
+#define SP_FILTER_H_SEEN
+
+/** \file
+ * SVG <filter> implementation, see sp-filter.cpp.
+ */
+/*
+ * Authors:
+ *   Hugo Rodrigues <haa.rodrigues@gmail.com>
+ *
+ * Copyright (C) 2006 Hugo Rodrigues
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "number-opt-number.h"
+#include "sp-object.h"
+#include "sp-filter-units.h"
+#include "svg/svg-length.h"
+
+/* Filter base class */
+
+/* MACROS DEFINED IN FILE sp-filter-fns.h */
+
+struct SPFilterReference;
+
+class SPFilter;
+class SPFilterClass;
+
+struct SPFilter : public SPObject {
+
+    /** filterUnits attribute */
+    SPFilterUnits filterUnits;
+    guint filterUnits_set : 1;
+    /** primitiveUnits attribute */
+    SPFilterUnits primitiveUnits;
+    guint primitiveUnits_set : 1;
+    /** X attribute */    
+    SVGLength x;
+    /** Y attribute */
+    SVGLength y;
+    /** WIDTH attribute */
+    SVGLength width;
+    /** HEIGHT attribute */
+    SVGLength height;
+    /** FILTERRES attribute */
+    NumberOptNumber filterRes;
+    /** HREF attribute */
+    SPFilterReference *href;
+};
+
+struct SPFilterClass {
+    SPObjectClass parent_class;
+};
+
+#include "sp-filter-fns.h"
+
+
+#endif /* !SP_FILTER_H_SEEN */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/sp-gaussian-blur-fns.h b/src/sp-gaussian-blur-fns.h
new file mode 100644 (file)
index 0000000..2dadd9e
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef SP_GAUSSIANBLUR_FNS_H
+#define SP_GAUSSIANBLUR_FNS_H
+
+/** \file 
+ * Macros and fn declarations related to gaussian blur filter.
+ */
+
+#include <glib-object.h>
+#include <glib/gtypes.h>
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+}
+
+class SPGaussianBlur;
+
+#define SP_TYPE_GAUSSIANBLUR (sp_gaussianBlur_get_type())
+#define SP_GAUSSIANBLUR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_GAUSSIANBLUR, SPGaussianBlur))
+#define SP_GAUSSIANBLUR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_GAUSSIANBLUR, SPGaussianBlurClass))
+#define SP_IS_GAUSSIANBLUR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_GAUSSIANBLUR))
+#define SP_IS_GAUSSIANBLUR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_GAUSSIANBLUR))
+
+GType sp_gaussianBlur_get_type();
+
+#endif /* !SP_GAUSSIANBLUR_FNS_H */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/sp-gaussian-blur.cpp b/src/sp-gaussian-blur.cpp
new file mode 100644 (file)
index 0000000..0e26fbb
--- /dev/null
@@ -0,0 +1,201 @@
+#define __SP_GAUSSIANBLUR_CPP__
+
+/** \file
+ * SVG <gaussianBlur> implementation.
+ *
+ */
+/*
+ * Authors:
+ *   hugo Rodrigues <haa.rodrigues@gmail.com>
+ *
+ * Copyright (C) 2006 Hugo Rodrigues
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "attributes.h"
+#include "svg/svg.h"
+#include "sp-gaussian-blur.h"
+#include "xml/repr.h"
+
+//#define SP_MACROS_SILENT
+//#include "macros.h"
+
+#define DEBUG_GAUSSIANBLUR
+#ifdef DEBUG_GAUSSIANBLUR
+# define debug(f, a...) { g_print("%s(%d) %s:", \
+                                  __FILE__,__LINE__,__FUNCTION__); \
+                          g_print(f, ## a); \
+                          g_print("\n"); \
+                        }
+#else
+# define debug(f, a...) /**/
+#endif
+
+/* GaussianBlur base class */
+
+static void sp_gaussianBlur_class_init(SPGaussianBlurClass *klass);
+static void sp_gaussianBlur_init(SPGaussianBlur *gaussianBlur);
+
+static void sp_gaussianBlur_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
+static void sp_gaussianBlur_release(SPObject *object);
+static void sp_gaussianBlur_set(SPObject *object, unsigned int key, gchar const *value);
+static void sp_gaussianBlur_update(SPObject *object, SPCtx *ctx, guint flags);
+static Inkscape::XML::Node *sp_gaussianBlur_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
+
+static SPObjectClass *gaussianBlur_parent_class;
+
+GType
+sp_gaussianBlur_get_type()
+{
+    static GType gaussianBlur_type = 0;
+
+    if (!gaussianBlur_type) {
+        GTypeInfo gaussianBlur_info = {
+            sizeof(SPGaussianBlurClass),
+            NULL, NULL,
+            (GClassInitFunc) sp_gaussianBlur_class_init,
+            NULL, NULL,
+            sizeof(SPGaussianBlur),
+            16,
+            (GInstanceInitFunc) sp_gaussianBlur_init,
+            NULL,    /* value_table */
+        };
+        gaussianBlur_type = g_type_register_static(SP_TYPE_OBJECT, "SPGaussianBlur", &gaussianBlur_info, (GTypeFlags)0);
+    }
+    return gaussianBlur_type;
+}
+
+static void
+sp_gaussianBlur_class_init(SPGaussianBlurClass *klass)
+{
+    SPObjectClass *sp_object_class = (SPObjectClass *)klass;
+
+    gaussianBlur_parent_class = (SPObjectClass*)g_type_class_peek_parent(klass);
+
+    sp_object_class->build = sp_gaussianBlur_build;
+    sp_object_class->release = sp_gaussianBlur_release;
+    sp_object_class->write = sp_gaussianBlur_write;
+    sp_object_class->set = sp_gaussianBlur_set;
+    sp_object_class->update = sp_gaussianBlur_update;
+}
+
+static void
+sp_gaussianBlur_init(SPGaussianBlur *gaussianBlur)
+{
+    debug("0x%p",gaussianBlur);
+
+//    gaussianBlur->stdDeviation = 1;
+}
+
+/**
+ * Reads the Inkscape::XML::Node, and initializes SPGaussianBlur variables.  For this to get called,
+ * our name must be associated with a repr via "sp_object_type_register".  Best done through
+ * sp-object-repr.cpp's repr_name_entries array.
+ */
+static void
+sp_gaussianBlur_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+    debug("0x%p",object);
+    if (((SPObjectClass *) gaussianBlur_parent_class)->build) {
+        ((SPObjectClass *) gaussianBlur_parent_class)->build(object, document, repr);
+    }
+
+    sp_object_read_attr(object, "stdDeviation");
+
+}
+
+/**
+ * Drops any allocated memory.
+ */
+static void
+sp_gaussianBlur_release(SPObject *object)
+{
+    debug("0x%p",object);
+
+    if (((SPObjectClass *) gaussianBlur_parent_class)->release)
+        ((SPObjectClass *) gaussianBlur_parent_class)->release(object);
+}
+
+/**
+ * Sets a specific value in the SPGaussianBlur.
+ */
+static void
+sp_gaussianBlur_set(SPObject *object, unsigned int key, gchar const *value)
+{
+    debug("0x%p %s(%u): '%s'",object,
+            sp_attribute_name(key),key,value);
+    SPGaussianBlur *gaussianBlur = SP_GAUSSIANBLUR(object);
+
+    switch(key) {
+       case SP_ATTR_STDDEVIATION:
+               gaussianBlur->stdDeviation.set(value);
+            break;
+        default:
+            if (((SPObjectClass *) gaussianBlur_parent_class)->set)
+                ((SPObjectClass *) gaussianBlur_parent_class)->set(object, key, value);
+            break;
+    }
+
+}
+
+/**
+ * Receives update notifications.
+ */
+static void
+sp_gaussianBlur_update(SPObject *object, SPCtx *ctx, guint flags)
+{
+    debug("0x%p",object);
+
+    if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
+                 SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
+
+        /* do something to trigger redisplay, updates? */
+
+    }
+
+    if (((SPObjectClass *) gaussianBlur_parent_class)->update) {
+        ((SPObjectClass *) gaussianBlur_parent_class)->update(object, ctx, flags);
+    }
+}
+
+/**
+ * Writes its settings to an incoming repr object, if any.
+ */
+static Inkscape::XML::Node *
+sp_gaussianBlur_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
+{
+    debug("0x%p",object);
+
+    // Inkscape-only object, not copied during an "plain SVG" dump:
+    if (flags & SP_OBJECT_WRITE_EXT) {
+        if (repr) {
+            // is this sane?
+            repr->mergeFrom(SP_OBJECT_REPR(object), "id");
+        } else {
+            repr = SP_OBJECT_REPR(object)->duplicate();
+        }
+    }
+
+    if (((SPObjectClass *) gaussianBlur_parent_class)->write) {
+        ((SPObjectClass *) gaussianBlur_parent_class)->write(object, repr, flags);
+    }
+
+    return repr;
+}
+
+
+/*
+  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:encoding=utf-8:textwidth=99 :
diff --git a/src/sp-gaussian-blur.h b/src/sp-gaussian-blur.h
new file mode 100644 (file)
index 0000000..e9ef50c
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef SP_GAUSSIANBLUR_H_SEEN
+#define SP_GAUSSIANBLUR_H_SEEN
+
+/** \file
+ * SVG <gaussianBlur> implementation, see sp-gaussianBlur.cpp.
+ */
+/*
+ * Authors:
+ *   Hugo Rodrigues <haa.rodrigues@gmail.com>
+ *
+ * Copyright (C) 2006 Hugo Rodrigues
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "sp-filter.h"
+#include "sp-gaussian-blur-fns.h"
+
+/* GaussianBlur base class */
+class SPGaussianBlurClass;
+
+struct SPGaussianBlur : public SPFilter {
+    /** stdDeviation attribute */
+    NumberOptNumber stdDeviation;
+};
+
+struct SPGaussianBlurClass {
+    SPFilterClass parent_class;
+};
+
+GType sp_gaussianBlur_get_type();
+
+
+#endif /* !SP_GAUSSIANBLUR_H_SEEN */
+
+/*
+  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:encoding=utf-8:textwidth=99 :
index 0f288a372a9600f562afc47c51a1db1c17eb183f..a4aa83ad2f569c85d74f4f79ee1209fb7ebaf460 100644 (file)
@@ -600,6 +600,13 @@ void CGroup::onUpdate(SPCtx *ctx, unsigned int flags) {
     if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
     flags &= SP_OBJECT_MODIFIED_CASCADE;
 
+    if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
+      SPObject *object = SP_OBJECT(_group);
+      for (SPItemView *v = SP_ITEM(_group)->display; v != NULL; v = v->next) {
+       nr_arena_group_set_style(NR_ARENA_GROUP(v->arenaitem), SP_OBJECT_STYLE(object));
+      }
+    }
+
     GSList *l = g_slist_reverse(_childList(true, ActionUpdate));
     while (l) {
         child = SP_OBJECT (l->data);
@@ -635,6 +642,13 @@ void CGroup::onModified(guint flags) {
     if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
     flags &= SP_OBJECT_MODIFIED_CASCADE;
 
+    if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
+      SPObject *object = SP_OBJECT(_group);
+      for (SPItemView *v = SP_ITEM(_group)->display; v != NULL; v = v->next) {
+       nr_arena_group_set_style(NR_ARENA_GROUP(v->arenaitem), SP_OBJECT_STYLE(object));
+      }
+    }
+
     GSList *l = g_slist_reverse(_childList(true));
     while (l) {
         child = SP_OBJECT (l->data);
@@ -691,11 +705,14 @@ gchar *CGroup::getDescription() {
 
 NRArenaItem *CGroup::show (NRArena *arena, unsigned int key, unsigned int flags) {
     NRArenaItem *ai;
+    SPObject *object = SP_OBJECT(_group);
 
     ai = NRArenaGroup::create(arena);
+
     nr_arena_group_set_transparent(NR_ARENA_GROUP (ai),
                                    _group->effectiveLayerMode(key) ==
                          SPGroup::LAYER);
+    nr_arena_group_set_style(NR_ARENA_GROUP(ai), SP_OBJECT_STYLE(object));
 
     _showChildren(arena, ai, key, flags);
     return ai;
index 0f2ff9e6a5f7439c318b85459116e726c2cd0ddc..4ffbce04f72ab32674d69ce6ffdfe72693f507bd 100644 (file)
@@ -43,6 +43,8 @@
 #include "sp-switch.h"
 #include "color-profile-fns.h"
 #include "xml/repr.h"
+#include "sp-filter.h"
+#include "sp-gaussian-blur.h"
 
 enum NameType { REPR_NAME, SODIPODI_TYPE };
 static unsigned const N_NAME_TYPES = SODIPODI_TYPE + 1;
@@ -100,6 +102,7 @@ populate_dtables()
         { "svg:clipPath", SP_TYPE_CLIPPATH },
         { "svg:defs", SP_TYPE_DEFS },
         { "svg:ellipse", SP_TYPE_ELLIPSE },
+        { "svg:filter", SP_TYPE_FILTER },
         /* Note: flow* elements are proposed additions for SVG 1.2, they aren't in
            SVG 1.1. */
         { "svg:flowDiv", SP_TYPE_FLOWDIV },
@@ -111,6 +114,7 @@ populate_dtables()
         { "svg:flowRoot", SP_TYPE_FLOWTEXT },
         { "svg:flowSpan", SP_TYPE_FLOWTSPAN },
         { "svg:g", SP_TYPE_GROUP },
+        { "svg:gaussianBlur", SP_TYPE_GAUSSIANBLUR },
         { "svg:image", SP_TYPE_IMAGE },
         { "svg:line", SP_TYPE_LINE },
         { "svg:linearGradient", SP_TYPE_LINEARGRADIENT },
index f9be549444d5986d804031f806a9f45edcf9c85a..c9811ec6e18420d291de0efbd875a55beaadf6ef 100644 (file)
@@ -375,6 +375,8 @@ sp_text_show(SPItem *item, NRArena *arena, unsigned /* key*/, unsigned /*flags*/
     NRArenaGroup *flowed = NRArenaGroup::create(arena);
     nr_arena_group_set_transparent (flowed, FALSE);
 
+    nr_arena_group_set_style(flowed, group->style);
+
     // pass the bbox of the text object as paintbox (used for paintserver fills)
     NRRect paintbox;
     sp_item_invoke_bbox(item, &paintbox, NR::identity(), TRUE);
index 2a0a30bd165b91fb4b12171307aa3bb3463b90da..d97dac316e031bb725be46e508234855817d27b3 100644 (file)
@@ -115,6 +115,7 @@ static void sp_style_clear(SPStyle *style);
 static void sp_style_merge_property(SPStyle *style, gint id, gchar const *val);
 
 static void sp_style_merge_ipaint(SPStyle *style, SPIPaint *paint, SPIPaint const *parent);
+static void sp_style_merge_ifilter(SPIFilter *child, SPIFilter const *parent);
 static void sp_style_read_dash(SPStyle *style, gchar const *str);
 
 static SPTextStyle *sp_text_style_new(void);
@@ -134,6 +135,7 @@ static void sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *s
 static void sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document);
 static void sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document);
 static void sp_style_read_ifontsize(SPIFontSize *val, gchar const *str);
+static void sp_style_read_ifilter(SPIFilter *f, gchar const *str, SPDocument *document);
 
 static void sp_style_read_penum(SPIEnum *val, Inkscape::XML::Node *repr, gchar const *key, SPStyleEnum const *dict, bool can_explicitly_inherit);
 static void sp_style_read_plength(SPILength *val, Inkscape::XML::Node *repr, gchar const *key);
@@ -648,6 +650,14 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr)
         }
     }
 
+    /* filter effects */
+    if (!style->filter.set) {
+        val = repr->attribute("filter");
+        if (val) {
+            sp_style_read_ifilter(&style->filter, val, (object) ? SP_OBJECT_DOCUMENT(object) : NULL);
+        }
+    }
+            
     /* 3. Merge from parent */
     if (object) {
         if (object->parent) {
@@ -875,12 +885,14 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val)
                 sp_style_read_iscale24(&style->opacity, val);
             }
             break;
-            /* Filter */
         case SP_PROP_ENABLE_BACKGROUND:
             g_warning("Unimplemented style property SP_PROP_ENABLE_BACKGROUND: value: %s", val);
             break;
+            /* Filter */
         case SP_PROP_FILTER:
-            g_warning("Unimplemented style property SP_PROP_FILTER: value: %s", val);
+            if (style->filter.set && style->filter.inherit) {
+                sp_style_read_ifilter(&style->filter, val, (style->object) ? SP_OBJECT_DOCUMENT(style->object) : NULL);
+            }
             break;
         case SP_PROP_FLOOD_COLOR:
             g_warning("Unimplemented style property SP_PROP_FLOOD_COLOR: value: %s", val);
@@ -1415,6 +1427,11 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent)
             style->marker[i].value = g_strdup(parent->marker[i].value);
         }
     }
+
+    /* Filter effects */
+    if(style->filter.set && style->filter.inherit) {
+        sp_style_merge_ifilter(&style->filter, &parent->filter);
+    }
 }
 
 template <typename T>
@@ -2042,6 +2059,19 @@ sp_style_merge_ipaint(SPStyle *style, SPIPaint *paint, SPIPaint const *parent)
 }
 
 
+/**
+ * Merge filter style from parent.
+ * Filter effects do not inherit by default
+ */
+static void
+sp_style_merge_ifilter(SPIFilter *child, SPIFilter const *parent)
+{
+    child->set = parent->set;
+    child->inherit = parent->inherit;
+    child->filter = parent->filter;
+    child->uri = parent->uri;
+}
+
 /**
  * Dumps the style to a CSS string, with either SP_STYLE_FLAG_IFSET or
  * SP_STYLE_FLAG_ALWAYS flags. Used with Always for copying an object's
@@ -2994,6 +3024,56 @@ sp_style_read_ifontsize(SPIFontSize *val, gchar const *str)
 
 
 
+/**
+ * Set SPIFilter object from string.
+ */
+static void
+sp_style_read_ifilter(SPIFilter *f, gchar const *str, SPDocument *document)
+{
+    /* Try all possible values: inherit, none, uri */
+    if (streq(str, "inherit")) {
+        f->set = TRUE;
+        f->inherit = TRUE;
+        f->filter = NULL;
+    } else if(streq(str, "none")) {
+        f->set = TRUE;
+        f->inherit = FALSE;
+        f->filter = NULL;
+    } else if (strneq(str, "url", 3)) {
+        f->uri = extract_uri(str);
+        if(f->uri == NULL || f->uri[0] == '\0') {
+            g_warning("Specified filter url is empty");
+            f->set = TRUE;
+            f->inherit = FALSE;
+            f->filter = NULL;
+            return;
+        }
+        f->set = TRUE;
+        f->inherit = FALSE;
+        f->filter = NULL;
+        if (document) {
+            SPObject *obj;
+            obj = sp_uri_reference_resolve(document, str);
+            if (SP_IS_FILTER(obj)) {
+                f->filter = SP_FILTER(obj);
+                //g_signal_connect(G_OBJECT(f->filter), "release",
+                //                 G_CALLBACK(sp_style_filter_release), style);
+                //g_signal_connect(G_OBJECT(f->filter), "modified",
+                //                 G_CALLBACK(sp_style_filter_modified), style);
+            } else {
+                g_warning("Element '%s' not found or is not a filter", f->uri);
+            }
+        }
+
+    } else {
+        /* We shouldn't reach this if SVG input is well-formed */
+        f->set = FALSE;
+        f->inherit = FALSE;
+        f->filter = NULL;
+        f->uri = NULL;
+    }
+}
+
 /**
  * Set SPIEnum object from repr attribute.
  */
index 703a55fdb9c8bfb3b0b2e69b66927a036c291088..3c070a22a6714281e644d6059ea1e42a8e94f122 100644 (file)
@@ -16,6 +16,7 @@
 #include "color.h"
 #include "forward.h"
 #include "sp-marker-loc.h"
+#include "sp-filter.h"
 
 namespace Inkscape {
 namespace XML {
@@ -166,6 +167,14 @@ struct SPIPaint {
     SVGICCColor *iccColor;
 };
 
+/// Filter type internal to SPStyle
+struct SPIFilter {
+    unsigned set : 1;
+    unsigned inherit : 1;
+    SPFilter *filter;
+    gchar *uri;
+};
+
 enum {
     SP_FONT_SIZE_LITERAL,
     SP_FONT_SIZE_LENGTH,
@@ -322,6 +331,9 @@ struct SPStyle {
     /** Marker list */
     SPIString marker[SP_MARKER_LOC_QTY];
 
+    /** Filter effect */
+    SPIFilter filter;
+
     /// style belongs to a cloned object, must not href anything
     bool cloned; 
     /// style has hreffed its fill/stroke paintservers, needs to release.