From: kiirala Date: Wed, 21 Jun 2006 16:04:22 +0000 (+0000) Subject: svg-filters branch merged back to head X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=36a048753a41b465ae130b361fb3b68c605e3e86;p=inkscape.git svg-filters branch merged back to head --- diff --git a/doc/nr-filter-interface.txt b/doc/nr-filter-interface.txt new file mode 100644 index 000000000..d5eb84a52 --- /dev/null +++ b/doc/nr-filter-interface.txt @@ -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) diff --git a/src/Makefile_insert b/src/Makefile_insert index ceecbd9ce..a0f2ff527 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -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 \ diff --git a/src/attributes.cpp b/src/attributes.cpp index 541acfc0c..f0ecc1e41 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -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"}, diff --git a/src/attributes.h b/src/attributes.h index bcbed50d0..d80e87006 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -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, diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index de6ec85c2..5aec6894f 100644 --- a/src/display/Makefile_insert +++ b/src/display/Makefile_insert @@ -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 diff --git a/src/display/nr-arena-glyphs.h b/src/display/nr-arena-glyphs.h index 23b74a378..a2dda988b 100644 --- a/src/display/nr-arena-glyphs.h +++ b/src/display/nr-arena-glyphs.h @@ -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; diff --git a/src/display/nr-arena-group.cpp b/src/display/nr-arena-group.cpp index f15c72e8e..9657715ea 100644 --- a/src/display/nr-arena-group.cpp +++ b/src/display/nr-arena-group.cpp @@ -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) { diff --git a/src/display/nr-arena-group.h b/src/display/nr-arena-group.h index b33495362..9da16908d 100644 --- a/src/display/nr-arena-group.h +++ b/src/display/nr-arena-group.h @@ -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(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 diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp index 97c769548..57413ef22 100644 --- a/src/display/nr-arena-item.cpp +++ b/src/display/nr-arena-item.cpp @@ -20,9 +20,12 @@ #include #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); diff --git a/src/display/nr-arena-item.h b/src/display/nr-arena-item.h index 22b992920..7a6c67602 100644 --- a/src/display/nr-arena-item.h +++ b/src/display/nr-arena-item.h @@ -58,6 +58,12 @@ #include #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 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; } diff --git a/src/display/nr-arena-shape.cpp b/src/display/nr-arena-shape.cpp index d26d1bc27..abe343a1d 100644 --- a/src/display/nr-arena-shape.cpp +++ b/src/display/nr-arena-shape.cpp @@ -16,6 +16,7 @@ #include #include +#include "display/nr-filter.h" #include #include #include @@ -26,6 +27,8 @@ #include #include #include +/* 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 index 000000000..e31d903f1 --- /dev/null +++ b/src/display/nr-filter-gaussian.cpp @@ -0,0 +1,394 @@ +#define __NR_FILTER_GAUSSIAN_CPP__ + +/* + * Gaussian blur renderer + * + * Author: + * Niko Kiirala + * + * Copyright (C) 2006 Niko Kiirala + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +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 index 000000000..445364a6c --- /dev/null +++ b/src/display/nr-filter-gaussian.h @@ -0,0 +1,83 @@ +#ifndef __NR_FILTER_GAUSSIAN_H__ +#define __NR_FILTER_GAUSSIAN_H__ + +/* + * Gaussian blur renderer + * + * Author: + * Niko Kiirala + * + * 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 index 000000000..6bdbf9a41 --- /dev/null +++ b/src/display/nr-filter-primitive.cpp @@ -0,0 +1,70 @@ +#define __NR_FILTER_PRIMITIVE_CPP__ + +/* + * SVG filters rendering + * + * Author: + * Niko Kiirala + * + * 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 index 000000000..a2bd14b61 --- /dev/null +++ b/src/display/nr-filter-primitive.h @@ -0,0 +1,108 @@ +#ifndef __NR_FILTER_PRIMITIVE_H__ +#define __NR_FILTER_PRIMITIVE_H__ + +/* + * SVG filters rendering + * + * Author: + * Niko Kiirala + * + * 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 index 000000000..a6f539d03 --- /dev/null +++ b/src/display/nr-filter-types.h @@ -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 index 000000000..35f348389 --- /dev/null +++ b/src/display/nr-filter.cpp @@ -0,0 +1,223 @@ +#define __NR_FILTER_CPP__ + +/* + * SVG filters rendering + * + * Author: + * Niko Kiirala + * + * 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 index 000000000..15a852d10 --- /dev/null +++ b/src/display/nr-filter.h @@ -0,0 +1,204 @@ +#ifndef __NR_FILTER_H__ +#define __NR_FILTER_H__ + +/* + * SVG filters rendering + * + * Author: + * Niko Kiirala + * + * 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 : diff --git a/src/libnr/nr-rect.cpp b/src/libnr/nr-rect.cpp index 0a8287bde..6d881e7b0 100644 --- a/src/libnr/nr-rect.cpp +++ b/src/libnr/nr-rect.cpp @@ -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) diff --git a/src/libnr/nr-rect.h b/src/libnr/nr-rect.h index 767eab902..ab78c1651 100644 --- a/src/libnr/nr-rect.h +++ b/src/libnr/nr-rect.h @@ -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 index 000000000..c08de58ef --- /dev/null +++ b/src/number-opt-number.h @@ -0,0 +1,108 @@ +#ifndef SEEN_NUMBER_OPT_NUMBER_H +#define SEEN_NUMBER_OPT_NUMBER_H + +/** \file + * implementation. + */ +/* + * Authors: + * Hugo Rodrigues + * + * Copyright (C) 2006 Hugo Rodrigues + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +//todo: use glib instead of stdlib +#include + +#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 index 000000000..21913c995 --- /dev/null +++ b/src/sp-filter-fns.h @@ -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 +#include +#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 index 000000000..18e187603 --- /dev/null +++ b/src/sp-filter-reference.cpp @@ -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 index 000000000..226e033bb --- /dev/null +++ b/src/sp-filter-reference.h @@ -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 index 000000000..1b6e7b7f9 --- /dev/null +++ b/src/sp-filter-units.h @@ -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 index 000000000..c3a4389c3 --- /dev/null +++ b/src/sp-filter.cpp @@ -0,0 +1,424 @@ +#define __SP_FILTER_CPP__ + +/** \file + * SVG implementation. + */ +/* + * Authors: + * Hugo Rodrigues + * + * 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 index 000000000..74cb1bfa8 --- /dev/null +++ b/src/sp-filter.h @@ -0,0 +1,70 @@ +#ifndef SP_FILTER_H_SEEN +#define SP_FILTER_H_SEEN + +/** \file + * SVG implementation, see sp-filter.cpp. + */ +/* + * Authors: + * Hugo Rodrigues + * + * 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 index 000000000..2dadd9e6e --- /dev/null +++ b/src/sp-gaussian-blur-fns.h @@ -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 +#include + +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 index 000000000..0e26fbbf4 --- /dev/null +++ b/src/sp-gaussian-blur.cpp @@ -0,0 +1,201 @@ +#define __SP_GAUSSIANBLUR_CPP__ + +/** \file + * SVG implementation. + * + */ +/* + * Authors: + * hugo Rodrigues + * + * 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 index 000000000..e9ef50c8c --- /dev/null +++ b/src/sp-gaussian-blur.h @@ -0,0 +1,45 @@ +#ifndef SP_GAUSSIANBLUR_H_SEEN +#define SP_GAUSSIANBLUR_H_SEEN + +/** \file + * SVG implementation, see sp-gaussianBlur.cpp. + */ +/* + * Authors: + * Hugo Rodrigues + * + * 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 : diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 0f288a372..a4aa83ad2 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -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; diff --git a/src/sp-object-repr.cpp b/src/sp-object-repr.cpp index 0f2ff9e6a..4ffbce04f 100644 --- a/src/sp-object-repr.cpp +++ b/src/sp-object-repr.cpp @@ -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 }, diff --git a/src/sp-text.cpp b/src/sp-text.cpp index f9be54944..c9811ec6e 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -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); diff --git a/src/style.cpp b/src/style.cpp index 2a0a30bd1..d97dac316 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -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 @@ -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. */ diff --git a/src/style.h b/src/style.h index 703a55fdb..3c070a22a 100644 --- a/src/style.h +++ b/src/style.h @@ -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.