From cfed8dd91c0f5615fdcafa903781b0ad3d9759e5 Mon Sep 17 00:00:00 2001 From: kiirala Date: Mon, 14 May 2007 12:54:58 +0000 Subject: [PATCH] Added renderer support for feBlend filter effect --- src/display/Makefile_insert | 2 + src/display/nr-arena-group.cpp | 8 + src/display/nr-arena-image.cpp | 8 + src/display/nr-arena-shape.cpp | 17 +- src/display/nr-filter-blend.cpp | 309 ++++++++++++++++++++++++++++++++ src/display/nr-filter-blend.h | 67 +++++++ src/display/nr-filter.cpp | 12 +- src/sp-feblend.cpp | 45 ++++- src/sp-feblend.h | 7 +- 9 files changed, 461 insertions(+), 14 deletions(-) create mode 100644 src/display/nr-filter-blend.cpp create mode 100644 src/display/nr-filter-blend.h diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index ad69c772b..95502170a 100644 --- a/src/display/Makefile_insert +++ b/src/display/Makefile_insert @@ -69,6 +69,8 @@ display_libspdisplay_a_SOURCES = \ display/nr-filter-primitive.h \ display/nr-filter-gaussian.cpp \ display/nr-filter-gaussian.h \ + display/nr-filter-blend.cpp \ + display/nr-filter-blend.h \ display/nr-filter-slot.cpp \ display/nr-filter-slot.h \ display/nr-filter-types.h \ diff --git a/src/display/nr-arena-group.cpp b/src/display/nr-arena-group.cpp index 0678eb37e..697ef6a05 100644 --- a/src/display/nr-arena-group.cpp +++ b/src/display/nr-arena-group.cpp @@ -19,6 +19,8 @@ #include "style.h" #include "sp-filter.h" #include "sp-gaussian-blur.h" +#include "sp-feblend.h" +#include "display/nr-filter-blend.h" static void nr_arena_group_class_init (NRArenaGroupClass *klass); static void nr_arena_group_init (NRArenaGroup *group); @@ -219,6 +221,12 @@ void nr_arena_group_set_style (NRArenaGroup *group, SPStyle *style) else gaussian->set_deviation((double) num); } + } else if(SP_IS_FEBLEND(primitive)) { + // TODO: this is just a test. Besides this whole filter + // creation needs to be redone + NR::FilterBlend *nrblend = (NR::FilterBlend *) group->filter->add_primitive(NR::NR_FILTER_BLEND); + SPFeBlend *spblend = SP_FEBLEND(primitive); + nrblend->set_mode(spblend->blend_mode); } } } diff --git a/src/display/nr-arena-image.cpp b/src/display/nr-arena-image.cpp index 1b4f3341c..964f7e86f 100644 --- a/src/display/nr-arena-image.cpp +++ b/src/display/nr-arena-image.cpp @@ -24,6 +24,8 @@ #include #include "sp-filter.h" #include "sp-gaussian-blur.h" +#include "sp-feblend.h" +#include "display/nr-filter-blend.h" int nr_arena_image_x_sample = 1; int nr_arena_image_y_sample = 1; @@ -392,6 +394,12 @@ void nr_arena_image_set_style (NRArenaImage *image, SPStyle *style) else gaussian->set_deviation((double) num); } + } else if(SP_IS_FEBLEND(primitive)) { + // TODO: this is just a test. Besides this whole filter + // creation needs to be redone + NR::FilterBlend *nrblend = (NR::FilterBlend *) image->filter->add_primitive(NR::NR_FILTER_BLEND); + SPFeBlend *spblend = SP_FEBLEND(primitive); + nrblend->set_mode(spblend->blend_mode); } } } diff --git a/src/display/nr-arena-shape.cpp b/src/display/nr-arena-shape.cpp index c5551695f..421627309 100644 --- a/src/display/nr-arena-shape.cpp +++ b/src/display/nr-arena-shape.cpp @@ -17,9 +17,6 @@ #include #include #include -#include "display/nr-filter.h" -#include "display/nr-filter-gaussian.h" -#include "display/nr-filter-types.h" #include #include #include @@ -32,9 +29,15 @@ #include #include "prefs-utils.h" #include "sp-filter.h" -#include "sp-gaussian-blur.h" #include "inkscape-cairo.h" +#include "display/nr-filter.h" +#include "display/nr-filter-gaussian.h" +#include "display/nr-filter-types.h" +#include "sp-gaussian-blur.h" +#include "sp-feblend.h" +#include "display/nr-filter-blend.h" + #include //int showRuns=0; @@ -1386,6 +1389,12 @@ nr_arena_shape_set_style(NRArenaShape *shape, SPStyle *style) else gaussian->set_deviation((double) num); } + } else if(SP_IS_FEBLEND(primitive)) { + // TODO: this is just a test. Besides this whole filter + // creation needs to be redone + NR::FilterBlend *nrblend = (NR::FilterBlend *) shape->filter->add_primitive(NR::NR_FILTER_BLEND); + SPFeBlend *spblend = SP_FEBLEND(primitive); + nrblend->set_mode(spblend->blend_mode); } } } diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp new file mode 100644 index 000000000..e36574d74 --- /dev/null +++ b/src/display/nr-filter-blend.cpp @@ -0,0 +1,309 @@ +/* + * SVG feBlend renderer + * + * "This filter composites two objects together using commonly used + * imaging software blending modes. It performs a pixel-wise combination + * of two input images." + * http://www.w3.org/TR/SVG11/filters.html#feBlend + * + * Authors: + * Niko Kiirala + * + * Copyright (C) 2007 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/nr-filter-blend.h" +#include "display/nr-filter-primitive.h" +#include "display/nr-filter-slot.h" +#include "display/nr-filter-types.h" +#include "libnr/nr-pixblock.h" +#include "libnr/nr-matrix.h" +#include "libnr/nr-blit.h" + +static inline int clamp(const int val, const int min, const int max) { + if (val < min) return min; + if (val > max) return max; + return val; +} + +template +static void _render(NRPixBlock &out, NRPixBlock &in1, NRPixBlock &in2) { + unsigned char *in1_data = NR_PIXBLOCK_PX(&in1); + unsigned char *in2_data = NR_PIXBLOCK_PX(&in2); + unsigned char *out_data = NR_PIXBLOCK_PX(&out); + unsigned char zero_rgba[4] = {0, 0, 0, 0}; + + if (in1.area.y0 < in2.area.y0) { + // in1 begins before in2 on y-axis + for (int y = in1.area.y0 ; y < in2.area.y0 ; y++) { + int out_line = (y - out.area.y0) * out.rs; + int in_line = (y - in1.area.y0) * in1.rs; + for (int x = in1.area.x0 ; x < in1.area.x1 ; x++) { + blend(out_data + out_line + 4 * (x - out.area.x0), + in1_data + in_line + 4 * (x - in1.area.x0), + zero_rgba); + } + } + } else if (in1.area.y0 > in2.area.y0) { + // in2 begins before in1 on y-axis + for (int y = in2.area.y0 ; y < in1.area.y0 ; y++) { + int out_line = (y - out.area.y0) * out.rs; + int in_line = (y - in2.area.y0) * in2.rs; + for (int x = in2.area.x0 ; x < in2.area.x1 ; x++) { + blend(out_data + out_line + 4 * (x - out.area.x0), + zero_rgba, + in2_data + in_line + 4 * (x - in2.area.x0)); + } + } + } + + for (int y = std::max(in1.area.y0, in2.area.y0) ; + y < std::min(in1.area.y1, in2.area.y1) ; ++y) { + int out_line = (y - out.area.y0) * out.rs; + int in1_line = (y - in1.area.y0) * in1.rs; + int in2_line = (y - in2.area.y0) * in2.rs; + + if (in1.area.x0 < in2.area.x0) { + // in1 begins before in2 on x-axis + for (int x = in1.area.x0 ; x < in2.area.x0 ; ++x) { + blend(out_data + out_line + 4 * (x - out.area.x0), + in1_data + in1_line + 4 * (x - in1.area.x0), + zero_rgba); + } + } else if (in1.area.x0 > in2.area.x0) { + // in2 begins before in1 on x-axis + for (int x = in2.area.x0 ; x < in1.area.x0 ; ++x) { + blend(out_data + out_line + 4 * (x - out.area.x0), + zero_rgba, + in2_data + in2_line + 4 * (x - in2.area.x0)); + } + } + + for (int x = std::max(in1.area.x0, in2.area.x0) ; + x < std::min(in1.area.x1, in2.area.x1) ; ++x) { + blend(out_data + out_line + 4 * (x - out.area.x0), + in1_data + in1_line + 4 * (x - in1.area.x0), + in2_data + in2_line + 4 * (x - in2.area.x0)); + } + + if (in1.area.x1 > in2.area.x1) { + // in1 ends after in2 on x-axis + for (int x = in2.area.x1 ; x < in1.area.x1 ; ++x) { + blend(out_data + out_line + 4 * (x - out.area.x0), + in1_data + in1_line + 4 * (x - in1.area.x0), + zero_rgba); + } + } else if (in1.area.x1 < in2.area.x1) { + // in2 ends after in1 on x-axis + for (int x = in1.area.x1 ; x < in2.area.x1 ; ++x) { + blend(out_data + out_line + 4 * (x - out.area.x0), + zero_rgba, + in2_data + in2_line + 4 * (x - in2.area.x0)); + } + } + } + + if (in1.area.y1 > in2.area.y1) { + // in1 ends after in2 on y-axis + for (int y = in2.area.y1 ; y < in1.area.y1 ; y++) { + int out_line = (y - out.area.y0) * out.rs; + int in_line = (y - in1.area.y0) * in1.rs; + for (int x = in1.area.x0 ; x < in1.area.x1 ; x++) { + blend(out_data + out_line + 4 * (x - out.area.x0), + in1_data + in_line + 4 * (x - in1.area.x0), + zero_rgba); + } + } + } else if (in1.area.y1 < in2.area.y1) { + // in2 ends after in1 on y-axis + for (int y = in1.area.y1 ; y < in2.area.y1 ; y++) { + int out_line = (y - out.area.y0) * out.rs; + int in_line = (y - in2.area.y0) * in2.rs; + for (int x = in2.area.x0 ; x < in2.area.x1 ; x++) { + blend(out_data + out_line + 4 * (x - out.area.x0), + zero_rgba, + in2_data + in_line + 4 * (x - in2.area.x0)); + } + } + } +} + +/* + * From http://www.w3.org/TR/SVG11/filters.html#feBlend + * + * For all feBlend modes, the result opacity is computed as follows: + * qr = 1 - (1-qa)*(1-qb) + * + * For the compositing formulas below, the following definitions apply: + * cr = Result color (RGB) - premultiplied + * qa = Opacity value at a given pixel for image A + * qb = Opacity value at a given pixel for image B + * ca = Color (RGB) at a given pixel for image A - premultiplied + * cb = Color (RGB) at a given pixel for image B - premultiplied +*/ + +// cr = (1 - qa) * cb + ca +inline void blend_normal(unsigned char *r, unsigned char const *a, unsigned char const *b) { + r[0] = (((255 - a[3]) * b[0]) >> 8) + a[0]; + r[1] = (((255 - a[3]) * b[1]) >> 8) + a[1]; + r[2] = (((255 - a[3]) * b[2]) >> 8) + a[2]; + r[3] = 255 - (((255 - a[3]) * (255 - b[3])) >> 8); +} + +// cr = (1-qa)*cb + (1-qb)*ca + ca*cb +inline void blend_multiply(unsigned char *r, unsigned char const *a, unsigned char const *b) { + r[0] = ((255 - a[3]) * b[0] + (255 - b[3]) * a[0] + a[0] * b[0]) >> 8; + r[1] = ((255 - a[3]) * b[1] + (255 - b[3]) * a[1] + a[1] * b[1]) >> 8; + r[2] = ((255 - a[3]) * b[2] + (255 - b[3]) * a[2] + a[2] * b[2]) >> 8; + r[3] = 255 - (((255 - a[3]) * (255 - b[3])) >> 8); +} + +// cr = cb + ca - ca * cb +inline void blend_screen(unsigned char *r, unsigned char const *a, unsigned char const *b) { + r[0] = b[0] + a[0] - ((a[0] * b[0]) >> 8); + r[1] = b[1] + a[1] - ((a[1] * b[1]) >> 8); + r[2] = b[2] + a[2] - ((a[2] * b[2]) >> 8); + r[3] = 255 - (((255 - a[3]) * (255 - b[3])) >> 8); +} + +// cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb) +inline void blend_darken(unsigned char *r, unsigned char const *a, unsigned char const *b) { + r[0] = std::min((((255 - a[3]) * b[0]) >> 8) + a[0], + (((255 - b[3]) * a[0]) >> 8) + b[0]); + r[1] = std::min((((255 - a[3]) * b[1]) >> 8) + a[1], + (((255 - b[3]) * a[1]) >> 8) + b[1]); + r[2] = std::min((((255 - a[3]) * b[2]) >> 8) + a[2], + (((255 - b[3]) * a[2]) >> 8) + b[2]); + r[3] = 255 - (((255 - a[3]) * (255 - b[3])) >> 8); +} + +// cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb) +inline void blend_lighten(unsigned char *r, unsigned char const *a, unsigned char const *b) { + r[0] = std::max((((255 - a[3]) * b[0]) >> 8) + a[0], + (((255 - b[3]) * a[0]) >> 8) + b[0]); + r[1] = std::max((((255 - a[3]) * b[1]) >> 8) + a[1], + (((255 - b[3]) * a[1]) >> 8) + b[1]); + r[2] = std::max((((255 - a[3]) * b[2]) >> 8) + a[2], + (((255 - b[3]) * a[2]) >> 8) + b[2]); + r[3] = 255 - (((255 - a[3]) * (255 - b[3])) >> 8); +} + +namespace NR { + +FilterBlend::FilterBlend() + : _blend_mode(BLEND_NORMAL), + _input2(NR_FILTER_SLOT_NOT_SET) +{} + +FilterPrimitive * FilterBlend::create() { + return new FilterBlend(); +} + +FilterBlend::~FilterBlend() +{} + +int FilterBlend::render(FilterSlot &slot, Matrix const &trans) { + NRPixBlock *in1 = slot.get(_input); + NRPixBlock *in2 = slot.get(_input2); + NRPixBlock *original_in1 = in1; + NRPixBlock *original_in2 = in2; + NRPixBlock *out; + + // Bail out if either one of source images is missing + if (!in1 || !in2) { + g_warning("Missing source image for feBlend (in=%d in2=%d)", _input, _input2); + return 1; + } + + out = new NRPixBlock; + NRRectL out_area; + nr_rect_l_union(&out_area, &in1->area, &in2->area); + nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P, + out_area.x0, out_area.y0, out_area.x1, out_area.y1, + true); + + // Blending modes are defined for premultiplied RGBA values, + // thus convert them to that format before blending + if (in1->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { + in1 = new NRPixBlock; + nr_pixblock_setup_fast(in1, NR_PIXBLOCK_MODE_R8G8B8A8P, + original_in1->area.x0, original_in1->area.y0, + original_in1->area.x1, original_in1->area.y1, + false); + nr_blit_pixblock_pixblock(in1, original_in1); + } + if (in2->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) { + in2 = new NRPixBlock; + nr_pixblock_setup_fast(in2, NR_PIXBLOCK_MODE_R8G8B8A8P, + original_in2->area.x0, original_in2->area.y0, + original_in2->area.x1, original_in2->area.y1, + false); + nr_blit_pixblock_pixblock(in2, original_in2); + } + + switch (_blend_mode) { + case BLEND_MULTIPLY: + _render(*out, *in1, *in2); + break; + case BLEND_SCREEN: + _render(*out, *in1, *in2); + break; + case BLEND_DARKEN: + _render(*out, *in1, *in2); + break; + case BLEND_LIGHTEN: + _render(*out, *in1, *in2); + break; + case BLEND_NORMAL: + default: + _render(*out, *in1, *in2); + break; + } + + if (in1 != original_in1) { + nr_pixblock_release(in1); + delete in1; + } + if (in2 != original_in2) { + nr_pixblock_release(in2); + delete in2; + } + + out->empty = FALSE; + slot.set(_output, out); + + return 0; +} + +void FilterBlend::set_input(int slot) { + _input = slot; +} + +void FilterBlend::set_input(int input, int slot) { + if (input == 0) _input = slot; + if (input == 1) _input2 = slot; +} + +void FilterBlend::set_mode(FilterBlendMode mode) { + if (mode == BLEND_NORMAL || mode == BLEND_MULTIPLY || + mode == BLEND_SCREEN || mode == BLEND_DARKEN || + mode == BLEND_LIGHTEN) + { + _blend_mode = mode; + } +} + +} /* 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-blend.h b/src/display/nr-filter-blend.h new file mode 100644 index 000000000..4787e336e --- /dev/null +++ b/src/display/nr-filter-blend.h @@ -0,0 +1,67 @@ +#ifndef __NR_FILTER_BLEND_H__ +#define __NR_FILTER_BLEND_H__ + +/* + * SVG feBlend renderer + * + * "This filter composites two objects together using commonly used + * imaging software blending modes. It performs a pixel-wise combination + * of two input images." + * http://www.w3.org/TR/SVG11/filters.html#feBlend + * + * Authors: + * Niko Kiirala + * + * Copyright (C) 2007 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/nr-filter-primitive.h" +#include "display/nr-filter-slot.h" +#include "libnr/nr-matrix.h" + +namespace NR { + +enum FilterBlendMode { + BLEND_NORMAL, + BLEND_MULTIPLY, + BLEND_SCREEN, + BLEND_DARKEN, + BLEND_LIGHTEN +}; + +class FilterBlend : public FilterPrimitive { +public: + FilterBlend(); + static FilterPrimitive *create(); + virtual ~FilterBlend(); + + virtual int render(FilterSlot &slot, Matrix const &trans); + + virtual void set_input(int slot); + virtual void set_input(int input, int slot); + void set_mode(FilterBlendMode mode); + +private: + FilterBlendMode _blend_mode; + int _input2; +}; + + +} /* namespace NR */ + + + + +#endif /* __NR_FILTER_BLEND_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 index 07c264928..fe33afff0 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -16,11 +16,12 @@ #include "display/nr-filter.h" #include "display/nr-filter-primitive.h" -#include "display/nr-filter-gaussian.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-types.h" #include "display/pixblock-scaler.h" #include "display/pixblock-transform.h" +#include "display/nr-filter-gaussian.h" +#include "display/nr-filter-blend.h" #include "display/nr-arena-item.h" #include "libnr/nr-pixblock.h" @@ -63,7 +64,7 @@ Filter::Filter() _primitive_count = 0; _primitive_table_size = 1; _primitive = new FilterPrimitive*[1]; - _primitive[0] = NULL; + _primitive[0] = NULL; //_primitive_count = 1; //_primitive[0] = new FilterGaussian; _common_init(); @@ -213,8 +214,9 @@ int Filter::render(NRArenaItem const *item, NRPixBlock *pb) slot.set(NR_FILTER_SOURCEGRAPHIC, in); in = NULL; // in is now handled by FilterSlot, we should not touch it - // TODO: loop through ALL the primitives and render them one at a time - _primitive[0]->render(slot, paraller_trans); + for (int i = 0 ; i < _primitive_count ; i++) { + _primitive[i]->render(slot, paraller_trans); + } NRPixBlock *out = slot.get(_output_slot); // Clear the pixblock, where the output will be put @@ -310,7 +312,7 @@ void Filter::_create_constructor_table() if(created) return; /* Filter effects not yet implemented are set to NULL */ - _constructor[NR_FILTER_BLEND] = NULL; + _constructor[NR_FILTER_BLEND] = &FilterBlend::create; _constructor[NR_FILTER_COLORMATRIX] = NULL; _constructor[NR_FILTER_COMPONENTTRANSFER] = NULL; _constructor[NR_FILTER_COMPOSITE] = NULL; diff --git a/src/sp-feblend.cpp b/src/sp-feblend.cpp index 7448dd4ab..55c387e00 100644 --- a/src/sp-feblend.cpp +++ b/src/sp-feblend.cpp @@ -6,9 +6,10 @@ */ /* * Authors: - * hugo Rodrigues + * Hugo Rodrigues + * Niko Kiirala * - * Copyright (C) 2006 Hugo Rodrigues + * Copyright (C) 2006,2007 authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -17,11 +18,14 @@ # include "config.h" #endif +#include + #include "attributes.h" #include "svg/svg.h" #include "sp-feblend.h" #include "xml/repr.h" +#include "display/nr-filter-blend.h" /* FeBlend base class */ @@ -89,6 +93,7 @@ sp_feBlend_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *re } /*LOAD ATTRIBUTES FROM REPR HERE*/ + sp_object_read_attr(object, "mode"); } /** @@ -101,6 +106,37 @@ sp_feBlend_release(SPObject *object) ((SPObjectClass *) feBlend_parent_class)->release(object); } +static NR::FilterBlendMode sp_feBlend_readmode(gchar const *value) +{ + if (!value) return NR::BLEND_NORMAL; + switch (value[0]) { + case 'n': + if (strncmp(value, "normal", 6) == 0) + return NR::BLEND_NORMAL; + break; + case 'm': + if (strncmp(value, "multiply", 8) == 0) + return NR::BLEND_MULTIPLY; + break; + case 's': + if (strncmp(value, "screen", 6) == 0) + return NR::BLEND_SCREEN; + break; + case 'd': + if (strncmp(value, "darken", 6) == 0) + return NR::BLEND_DARKEN; + break; + case 'l': + if (strncmp(value, "lighten", 7) == 0) + return NR::BLEND_LIGHTEN; + break; + default: + // do nothing by default + break; + } + return NR::BLEND_NORMAL; +} + /** * Sets a specific value in the SPFeBlend. */ @@ -112,6 +148,9 @@ sp_feBlend_set(SPObject *object, unsigned int key, gchar const *value) switch(key) { /*DEAL WITH SETTING ATTRIBUTES HERE*/ + case SP_ATTR_MODE: + feBlend->blend_mode = sp_feBlend_readmode(value); + break; default: if (((SPObjectClass *) feBlend_parent_class)->set) ((SPObjectClass *) feBlend_parent_class)->set(object, key, value); @@ -148,7 +187,7 @@ sp_feBlend_write(SPObject *object, Inkscape::XML::Node *repr, guint flags) if (flags & SP_OBJECT_WRITE_EXT) { if (repr) { // is this sane? - repr->mergeFrom(SP_OBJECT_REPR(object), "id"); + // repr->mergeFrom(SP_OBJECT_REPR(object), "id"); } else { repr = SP_OBJECT_REPR(object)->duplicate(NULL); // FIXME } diff --git a/src/sp-feblend.h b/src/sp-feblend.h index dea1100a3..90ab8967f 100644 --- a/src/sp-feblend.h +++ b/src/sp-feblend.h @@ -7,8 +7,9 @@ /* * Authors: * Hugo Rodrigues + * Niko Kiirala * - * Copyright (C) 2006 Hugo Rodrigues + * Copyright (C) 2006,2007 authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -16,12 +17,14 @@ #include "sp-filter.h" #include "sp-feblend-fns.h" +#include "display/nr-filter-blend.h" + /* FeBlend base class */ class SPFeBlendClass; struct SPFeBlend : public SPFilterPrimitive { /** BLEND ATTRIBUTES HERE */ - + NR::FilterBlendMode blend_mode; }; struct SPFeBlendClass { -- 2.30.2