summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNiko Kiirala <niko@kiirala.com>2007-07-09 12:04:51 +0000
committerkiirala <kiirala@users.sourceforge.net>2007-07-09 12:04:51 +0000
commitd07f036c6e1861995750fa58322b561060eba0ee (patch)
treed1d1c10e592d5e69e28ce36e7c1262328dbea2af /src
parentfix 1731457 (diff)
downloadinkscape-d07f036c6e1861995750fa58322b561060eba0ee.tar.gz
inkscape-d07f036c6e1861995750fa58322b561060eba0ee.zip
First version of feComposite filter effect renderer
(bzr r3201)
Diffstat (limited to 'src')
-rw-r--r--src/display/Makefile_insert3
-rw-r--r--src/display/nr-filter-blend.cpp139
-rw-r--r--src/display/nr-filter-composite.cpp235
-rw-r--r--src/display/nr-filter-composite.h54
-rw-r--r--src/display/nr-filter-pixops.h148
-rw-r--r--src/display/nr-filter-skeleton.cpp2
-rw-r--r--src/display/nr-filter-skeleton.h2
-rw-r--r--src/display/nr-filter.cpp4
-rw-r--r--src/sp-fecomposite.cpp127
-rw-r--r--src/sp-fecomposite.h17
-rw-r--r--src/sp-feoffset.cpp3
11 files changed, 612 insertions, 122 deletions
diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert
index 462da04c5..1a4c01011 100644
--- a/src/display/Makefile_insert
+++ b/src/display/Makefile_insert
@@ -73,10 +73,13 @@ display_libspdisplay_a_SOURCES = \
display/nr-filter-blend.h \
display/nr-filter-offset.cpp \
display/nr-filter-offset.h \
+ display/nr-filter-composite.h \
+ display/nr-filter-composite.cpp \
display/nr-filter-slot.cpp \
display/nr-filter-slot.h \
display/nr-filter-getalpha.cpp \
display/nr-filter-getalpha.h \
+ display/nr-filter-pixops.h \
display/nr-filter-types.h \
display/pixblock-scaler.cpp \
display/pixblock-scaler.h \
diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp
index 0e2f965b7..b02b20cbf 100644
--- a/src/display/nr-filter-blend.cpp
+++ b/src/display/nr-filter-blend.cpp
@@ -15,6 +15,7 @@
*/
#include "display/nr-filter-blend.h"
+#include "display/nr-filter-pixops.h"
#include "display/nr-filter-primitive.h"
#include "display/nr-filter-slot.h"
#include "display/nr-filter-types.h"
@@ -23,107 +24,7 @@
#include "libnr/nr-blit.h"
#include "libnr/nr-pixops.h"
-template <void(*blend)(unsigned char *cr, unsigned char const *ca, unsigned char const *cb)>
-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));
- }
- }
- }
-}
+namespace NR {
/*
* From http://www.w3.org/TR/SVG11/filters.html#feBlend
@@ -152,7 +53,9 @@ static void _render(NRPixBlock &out, NRPixBlock &in1, NRPixBlock &in2) {
#define SET_ALPHA r[3] = NR_NORMALIZE_21((255 * 255) - (255 - a[3]) * (255 - b[3]))
// cr = (1 - qa) * cb + ca
-inline void blend_normal(unsigned char *r, unsigned char const *a, unsigned char const *b) {
+inline void
+blend_normal(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
r[0] = NR_NORMALIZE_21((255 - a[3]) * b[0]) + a[0];
r[1] = NR_NORMALIZE_21((255 - a[3]) * b[1]) + a[1];
r[2] = NR_NORMALIZE_21((255 - a[3]) * b[2]) + a[2];
@@ -160,7 +63,9 @@ inline void blend_normal(unsigned char *r, unsigned char const *a, unsigned char
}
// cr = (1-qa)*cb + (1-qb)*ca + ca*cb
-inline void blend_multiply(unsigned char *r, unsigned char const *a, unsigned char const *b) {
+inline void
+blend_multiply(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
r[0] = NR_NORMALIZE_21((255 - a[3]) * b[0] + (255 - b[3]) * a[0]
+ a[0] * b[0]);
r[1] = NR_NORMALIZE_21((255 - a[3]) * b[1] + (255 - b[3]) * a[1]
@@ -171,7 +76,9 @@ inline void blend_multiply(unsigned char *r, unsigned char const *a, unsigned ch
}
// cr = cb + ca - ca * cb
-inline void blend_screen(unsigned char *r, unsigned char const *a, unsigned char const *b) {
+inline void
+blend_screen(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
r[0] = NR_NORMALIZE_21(b[0] * 255 + a[0] * 255 - a[0] * b[0]);
r[1] = NR_NORMALIZE_21(b[1] * 255 + a[1] * 255 - a[1] * b[1]);
r[2] = NR_NORMALIZE_21(b[2] * 255 + a[2] * 255 - a[2] * b[2]);
@@ -179,7 +86,9 @@ inline void blend_screen(unsigned char *r, unsigned char const *a, unsigned char
}
// 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) {
+inline void
+blend_darken(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
r[0] = std::min(NR_NORMALIZE_21((255 - a[3]) * b[0]) + a[0],
NR_NORMALIZE_21((255 - b[3]) * a[0]) + b[0]);
r[1] = std::min(NR_NORMALIZE_21((255 - a[3]) * b[1]) + a[1],
@@ -190,7 +99,9 @@ inline void blend_darken(unsigned char *r, unsigned char const *a, unsigned char
}
// 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) {
+inline void
+blend_lighten(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
r[0] = std::max(NR_NORMALIZE_21((255 - a[3]) * b[0]) + a[0],
NR_NORMALIZE_21((255 - b[3]) * a[0]) + b[0]);
r[1] = std::max(NR_NORMALIZE_21((255 - a[3]) * b[1]) + a[1],
@@ -200,8 +111,6 @@ inline void blend_lighten(unsigned char *r, unsigned char const *a, unsigned cha
SET_ALPHA;
}
-namespace NR {
-
FilterBlend::FilterBlend()
: _blend_mode(BLEND_NORMAL),
_input2(NR_FILTER_SLOT_NOT_SET)
@@ -253,22 +162,26 @@ int FilterBlend::render(FilterSlot &slot, Matrix const &trans) {
nr_blit_pixblock_pixblock(in2, original_in2);
}
+ /* pixops_mix is defined in display/nr-filter-pixops.h
+ * It mixes the two input images with the function given as template
+ * and places the result in output image.
+ */
switch (_blend_mode) {
case BLEND_MULTIPLY:
- _render<blend_multiply>(*out, *in1, *in2);
+ pixops_mix<blend_multiply>(*out, *in1, *in2);
break;
case BLEND_SCREEN:
- _render<blend_screen>(*out, *in1, *in2);
+ pixops_mix<blend_screen>(*out, *in1, *in2);
break;
case BLEND_DARKEN:
- _render<blend_darken>(*out, *in1, *in2);
+ pixops_mix<blend_darken>(*out, *in1, *in2);
break;
case BLEND_LIGHTEN:
- _render<blend_lighten>(*out, *in1, *in2);
+ pixops_mix<blend_lighten>(*out, *in1, *in2);
break;
case BLEND_NORMAL:
default:
- _render<blend_normal>(*out, *in1, *in2);
+ pixops_mix<blend_normal>(*out, *in1, *in2);
break;
}
diff --git a/src/display/nr-filter-composite.cpp b/src/display/nr-filter-composite.cpp
new file mode 100644
index 000000000..43580191e
--- /dev/null
+++ b/src/display/nr-filter-composite.cpp
@@ -0,0 +1,235 @@
+/*
+ * feComposite filter effect renderer
+ *
+ * Authors:
+ * Niko Kiirala <niko@kiirala.com>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <cmath>
+
+#include "isnan.h"
+#include "sp-fecomposite.h"
+#include "display/nr-filter-composite.h"
+#include "display/nr-filter-pixops.h"
+#include "display/nr-filter-slot.h"
+#include "libnr/nr-blit.h"
+#include "libnr/nr-pixblock.h"
+#include "libnr/nr-pixops.h"
+#include "libnr/nr-matrix.h"
+
+inline void
+composite_over(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
+ r[0] = a[0] + NR_NORMALIZE_21(b[0] * (255 - a[3]));
+ r[1] = a[1] + NR_NORMALIZE_21(b[1] * (255 - a[3]));
+ r[2] = a[2] + NR_NORMALIZE_21(b[2] * (255 - a[3]));
+ r[3] = a[3] + NR_NORMALIZE_21(b[3] * (255 - a[3]));
+}
+
+inline void
+composite_in(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
+ r[0] = NR_NORMALIZE_21(a[0] * b[3]);
+ r[1] = NR_NORMALIZE_21(a[1] * b[3]);
+ r[2] = NR_NORMALIZE_21(a[2] * b[3]);
+ r[3] = NR_NORMALIZE_21(a[3] * b[3]);
+}
+
+inline void
+composite_out(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
+ r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3]));
+ r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3]));
+ r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3]));
+ r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3]));
+}
+
+inline void
+composite_atop(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
+ r[0] = NR_NORMALIZE_21(a[0] * b[3] + b[0] * (255 - a[3]));
+ r[1] = NR_NORMALIZE_21(a[1] * b[3] + b[1] * (255 - a[3]));
+ r[2] = NR_NORMALIZE_21(a[2] * b[3] + b[2] * (255 - a[3]));
+ r[3] = b[3];
+}
+
+inline void
+composite_xor(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
+ r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3]) + b[0] * (255 - a[3]));
+ r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3]) + b[1] * (255 - a[3]));
+ r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3]) + b[2] * (255 - a[3]));
+ r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3]) + b[3] * (255 - a[3]));
+}
+
+static int clamp(int val) {
+ if (val < 0) return 0;
+ if (val > 255) return 255;
+ return val;
+}
+
+// BUGBUG / TODO
+// This makes arithmetic compositing non re-entrant and non thread safe.
+static int arith_k1, arith_k2, arith_k3, arith_k4;
+inline void
+composite_arithmetic(unsigned char *r, unsigned char const *a, unsigned char const *b)
+{
+ r[0] = clamp(NR_NORMALIZE_31(arith_k1 * a[0] * b[0]
+ + arith_k2 * a[0] + arith_k3 * b[0] + arith_k4));
+ r[1] = clamp(NR_NORMALIZE_31(arith_k1 * a[1] * b[1]
+ + arith_k2 * a[1] + arith_k3 * b[1] + arith_k4));
+ r[2] = clamp(NR_NORMALIZE_31(arith_k1 * a[2] * b[2]
+ + arith_k2 * a[2] + arith_k3 * b[2] + arith_k4));
+ r[3] = clamp(NR_NORMALIZE_31(arith_k1 * a[3] * b[3]
+ + arith_k2 * a[3] + arith_k3 * b[3] + arith_k4));
+}
+
+namespace NR {
+
+FilterComposite::FilterComposite() :
+ op(COMPOSITE_DEFAULT), k1(0), k2(0), k3(0), k4(0),
+ _input2(NR::NR_FILTER_SLOT_NOT_SET)
+{}
+
+FilterPrimitive * FilterComposite::create() {
+ return new FilterComposite();
+}
+
+FilterComposite::~FilterComposite()
+{}
+
+int FilterComposite::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 feComposite (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);
+ }
+
+ /* pixops_mix is defined in display/nr-filter-pixops.h
+ * It mixes the two input images with the function given as template
+ * and places the result in output image.
+ */
+ switch (op) {
+ case COMPOSITE_IN:
+ pixops_mix<composite_in>(*out, *in1, *in2);
+ break;
+ case COMPOSITE_OUT:
+ pixops_mix<composite_out>(*out, *in1, *in2);
+ break;
+ case COMPOSITE_ATOP:
+ pixops_mix<composite_atop>(*out, *in1, *in2);
+ break;
+ case COMPOSITE_XOR:
+ pixops_mix<composite_xor>(*out, *in1, *in2);
+ break;
+ case COMPOSITE_ARITHMETIC:
+ arith_k1 = (int)(k1 * 255);
+ arith_k2 = (int)(k2 * 255 * 255);
+ arith_k3 = (int)(k3 * 255 * 255);
+ arith_k4 = (int)(k4 * 255 * 255 * 255);
+ pixops_mix<composite_arithmetic>(*out, *in1, *in2);
+ break;
+ case COMPOSITE_DEFAULT:
+ case COMPOSITE_OVER:
+ default:
+ pixops_mix<composite_over>(*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 FilterComposite::set_input(int input) {
+ _input = input;
+}
+
+void FilterComposite::set_input(int input, int slot) {
+ if (input == 0) _input = slot;
+ if (input == 1) _input2 = slot;
+}
+
+void FilterComposite::set_operator(FeCompositeOperator op) {
+ if (op == COMPOSITE_DEFAULT) {
+ this->op = COMPOSITE_OVER;
+ } else if (op == COMPOSITE_OVER ||
+ op == COMPOSITE_IN ||
+ op == COMPOSITE_OUT ||
+ op == COMPOSITE_ATOP ||
+ op == COMPOSITE_XOR ||
+ op == COMPOSITE_ARITHMETIC)
+ {
+ this->op = op;
+ }
+}
+
+void FilterComposite::set_arithmetic(double k1, double k2, double k3, double k4) {
+ if (!isFinite(k1) || !isFinite(k2) || !isFinite(k3) || !isFinite(k4)) {
+ g_warning("Non-finite parameter for feComposite arithmetic operator");
+ return;
+ }
+ this->k1 = k1;
+ this->k2 = k2;
+ this->k3 = k3;
+ this->k4 = k4;
+}
+
+} /* 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-composite.h b/src/display/nr-filter-composite.h
new file mode 100644
index 000000000..a1bbbb186
--- /dev/null
+++ b/src/display/nr-filter-composite.h
@@ -0,0 +1,54 @@
+#ifndef __NR_FILTER_COMPOSITE_H__
+#define __NR_FILTER_COMPOSITE_H__
+
+/*
+ * feComposite filter effect renderer
+ *
+ * Authors:
+ * Niko Kiirala <niko@kiirala.com>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "sp-fecomposite.h"
+#include "display/nr-filter-primitive.h"
+#include "display/nr-filter-slot.h"
+#include "libnr/nr-matrix.h"
+
+namespace NR {
+
+class FilterComposite : public FilterPrimitive {
+public:
+ FilterComposite();
+ static FilterPrimitive *create();
+ virtual ~FilterComposite();
+
+ virtual int render(FilterSlot &slot, Matrix const &trans);
+
+ virtual void set_input(int input);
+ virtual void set_input(int input, int slot);
+
+ void set_operator(FeCompositeOperator op);
+ void set_arithmetic(double k1, double k2, double k3, double k4);
+
+private:
+ FeCompositeOperator op;
+ double k1, k2, k3, k4;
+ int _input2;
+};
+
+} /* namespace NR */
+
+#endif /* __NR_FILTER_COMPOSITE_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-pixops.h b/src/display/nr-filter-pixops.h
new file mode 100644
index 000000000..2baf3422b
--- /dev/null
+++ b/src/display/nr-filter-pixops.h
@@ -0,0 +1,148 @@
+#ifndef __NR_FILTER_PIXOPS_H__
+#define __NR_FILTER_PIXOPS_H__
+
+/*
+ * Per-pixel image manipulation functions.
+ * These can be used by all filter primitives, which combine two images on
+ * per-pixel basis. These are at least feBlend, feComposite and feMerge.
+ *
+ * Authors:
+ * Niko Kiirala <niko@kiirala.com>
+ *
+ * Copyright (C) 2007 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+namespace NR {
+
+/**
+ * Mixes the two input images using the function given as template.
+ * The result is placed in out.
+ * The mixing function should have the following type:
+ * void mix(unsigned char *result, unsigned char const *in1,
+ * unsigned char const *in2);
+ * Each of the parameters for mix-function is a pointer to four bytes of data,
+ * giving the RGBA values for that pixel. The mix function must only access
+ * the four bytes beginning at a pointer given as parameter.
+ */
+/*
+ * The implementation is in a header file because of the template. It has to
+ * be in the same compilation unit as the code using it. Otherwise, linking
+ * the program will not succeed.
+ */
+template <void(*blend)(unsigned char *cr, unsigned char const *ca, unsigned char const *cb)>
+void pixops_mix(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));
+ }
+ }
+ }
+}
+
+} // namespace NR
+
+#endif // __NR_FILTER_PIXOPS_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-skeleton.cpp b/src/display/nr-filter-skeleton.cpp
index f595f13f1..68c38c4b2 100644
--- a/src/display/nr-filter-skeleton.cpp
+++ b/src/display/nr-filter-skeleton.cpp
@@ -5,7 +5,7 @@
* type, like gaussian or blend in respective case.
*
* This can be accomplished with the following sed command:
- * sed -e "s/Skeleton/Name/g" -e "s/skeleton/name" -e "s/SKELETON/NAME"
+ * sed -e "s/Skeleton/Name/g" -e "s/skeleton/name/" -e "s/SKELETON/NAME/"
* nr-filter-skeleton.cpp >nr-filter-name.cpp
*
* (on one line, replace occurences of 'name' with your filter name)
diff --git a/src/display/nr-filter-skeleton.h b/src/display/nr-filter-skeleton.h
index ae3a08046..edba5c2f1 100644
--- a/src/display/nr-filter-skeleton.h
+++ b/src/display/nr-filter-skeleton.h
@@ -8,7 +8,7 @@
* type, like gaussian or blend in respective case.
*
* This can be accomplished with the following sed command:
- * sed -e "s/Skeleton/Name/g" -e "s/skeleton/name" -e "s/SKELETON/NAME"
+ * sed -e "s/Skeleton/Name/g" -e "s/skeleton/name/" -e "s/SKELETON/NAME/"
* nr-filter-skeleton.h >nr-filter-name.h
*
* (on one line, replace occurences of 'name' with your filter name)
diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp
index 33e89ac3e..61571f43b 100644
--- a/src/display/nr-filter.cpp
+++ b/src/display/nr-filter.cpp
@@ -20,9 +20,11 @@
#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-filter-offset.h"
+#include "display/nr-filter-composite.h"
#include "display/nr-arena-item.h"
#include "libnr/nr-pixblock.h"
@@ -309,7 +311,7 @@ void Filter::_create_constructor_table()
_constructor[NR_FILTER_BLEND] = &FilterBlend::create;
_constructor[NR_FILTER_COLORMATRIX] = NULL;
_constructor[NR_FILTER_COMPONENTTRANSFER] = NULL;
- _constructor[NR_FILTER_COMPOSITE] = NULL;
+ _constructor[NR_FILTER_COMPOSITE] = &FilterComposite::create;
_constructor[NR_FILTER_CONVOLVEMATRIX] = NULL;
_constructor[NR_FILTER_DIFFUSELIGHTING] = NULL;
_constructor[NR_FILTER_DISPLACEMENTMAP] = NULL;
diff --git a/src/sp-fecomposite.cpp b/src/sp-fecomposite.cpp
index 86839aab5..68312aa1b 100644
--- a/src/sp-fecomposite.cpp
+++ b/src/sp-fecomposite.cpp
@@ -21,7 +21,7 @@
#include "svg/svg.h"
#include "sp-fecomposite.h"
#include "xml/repr.h"
-
+#include "display/nr-filter-composite.h"
/* FeComposite base class */
@@ -33,6 +33,7 @@ static void sp_feComposite_release(SPObject *object);
static void sp_feComposite_set(SPObject *object, unsigned int key, gchar const *value);
static void sp_feComposite_update(SPObject *object, SPCtx *ctx, guint flags);
static Inkscape::XML::Node *sp_feComposite_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
+static void sp_feComposite_build_renderer(SPFilterPrimitive *primitive, NR::Filter *filter);
static SPFilterPrimitiveClass *feComposite_parent_class;
@@ -61,6 +62,7 @@ static void
sp_feComposite_class_init(SPFeCompositeClass *klass)
{
SPObjectClass *sp_object_class = (SPObjectClass *)klass;
+ SPFilterPrimitiveClass *sp_primitive_class = (SPFilterPrimitiveClass *)klass;
feComposite_parent_class = (SPFilterPrimitiveClass*)g_type_class_peek_parent(klass);
@@ -69,11 +71,19 @@ sp_feComposite_class_init(SPFeCompositeClass *klass)
sp_object_class->write = sp_feComposite_write;
sp_object_class->set = sp_feComposite_set;
sp_object_class->update = sp_feComposite_update;
+
+ sp_primitive_class->build_renderer = sp_feComposite_build_renderer;
}
static void
sp_feComposite_init(SPFeComposite *feComposite)
{
+ feComposite->composite_operator = COMPOSITE_DEFAULT;
+ feComposite->k1 = 0;
+ feComposite->k2 = 0;
+ feComposite->k3 = 0;
+ feComposite->k4 = 0;
+ feComposite->in2 = NR::NR_FILTER_SLOT_NOT_SET;
}
/**
@@ -88,7 +98,16 @@ sp_feComposite_build(SPObject *object, SPDocument *document, Inkscape::XML::Node
((SPObjectClass *) feComposite_parent_class)->build(object, document, repr);
}
- /*LOAD ATTRIBUTES FROM REPR HERE*/
+ SPFeComposite *composite = SP_FECOMPOSITE(object);
+
+ sp_object_read_attr(object, "operator");
+ if (composite->composite_operator == COMPOSITE_ARITHMETIC) {
+ sp_object_read_attr(object, "k1");
+ sp_object_read_attr(object, "k2");
+ sp_object_read_attr(object, "k3");
+ sp_object_read_attr(object, "k4");
+ }
+ sp_object_read_attr(object, "in2");
}
/**
@@ -101,6 +120,32 @@ sp_feComposite_release(SPObject *object)
((SPObjectClass *) feComposite_parent_class)->release(object);
}
+static double
+sp_feComposite_read_number(gchar const *value) {
+ char *end;
+ double ret = g_ascii_strtod(value, &end);
+ if (*end) {
+ g_warning("Unable to convert \"%s\" to number", value);
+ // We could leave this out, too. If strtod can't convert
+ // anything, it will return zero.
+ ret = 0;
+ }
+ return ret;
+}
+
+static FeCompositeOperator
+sp_feComposite_read_operator(gchar const *value) {
+ if (!value) return COMPOSITE_DEFAULT;
+
+ if (strcmp(value, "over") == 0) return COMPOSITE_OVER;
+ else if (strcmp(value, "in") == 0) return COMPOSITE_IN;
+ else if (strcmp(value, "out") == 0) return COMPOSITE_OUT;
+ else if (strcmp(value, "atop") == 0) return COMPOSITE_ATOP;
+ else if (strcmp(value, "xor") == 0) return COMPOSITE_XOR;
+ else if (strcmp(value, "arithmetic") == 0) return COMPOSITE_ARITHMETIC;
+ return COMPOSITE_DEFAULT;
+}
+
/**
* Sets a specific value in the SPFeComposite.
*/
@@ -110,8 +155,63 @@ sp_feComposite_set(SPObject *object, unsigned int key, gchar const *value)
SPFeComposite *feComposite = SP_FECOMPOSITE(object);
(void)feComposite;
+ int input;
+ FeCompositeOperator op;
+ double k_n;
switch(key) {
/*DEAL WITH SETTING ATTRIBUTES HERE*/
+ case SP_ATTR_OPERATOR:
+ op = sp_feComposite_read_operator(value);
+ if (op != feComposite->composite_operator) {
+ feComposite->composite_operator = op;
+ object->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ }
+ break;
+
+ case SP_ATTR_K1:
+ k_n = sp_feComposite_read_number(value);
+ if (k_n != feComposite->k1) {
+ feComposite->k1 = k_n;
+ if (feComposite->composite_operator == COMPOSITE_ARITHMETIC)
+ object->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ }
+ break;
+
+ case SP_ATTR_K2:
+ k_n = sp_feComposite_read_number(value);
+ if (k_n != feComposite->k2) {
+ feComposite->k2 = k_n;
+ if (feComposite->composite_operator == COMPOSITE_ARITHMETIC)
+ object->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ }
+ break;
+
+ case SP_ATTR_K3:
+ k_n = sp_feComposite_read_number(value);
+ if (k_n != feComposite->k3) {
+ feComposite->k3 = k_n;
+ if (feComposite->composite_operator == COMPOSITE_ARITHMETIC)
+ object->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ }
+ break;
+
+ case SP_ATTR_K4:
+ k_n = sp_feComposite_read_number(value);
+ if (k_n != feComposite->k4) {
+ feComposite->k4 = k_n;
+ if (feComposite->composite_operator == COMPOSITE_ARITHMETIC)
+ object->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ }
+ break;
+
+ case SP_ATTR_IN2:
+ input = sp_filter_primitive_read_in(feComposite, value);
+ if (input != feComposite->in2) {
+ feComposite->in2 = input;
+ object->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ }
+ break;
+
default:
if (((SPObjectClass *) feComposite_parent_class)->set)
((SPObjectClass *) feComposite_parent_class)->set(object, key, value);
@@ -148,7 +248,7 @@ sp_feComposite_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
}
@@ -161,6 +261,27 @@ sp_feComposite_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
return repr;
}
+static void sp_feComposite_build_renderer(SPFilterPrimitive *primitive, NR::Filter *filter) {
+ g_assert(primitive != NULL);
+ g_assert(filter != NULL);
+
+ SPFeComposite *sp_composite = SP_FECOMPOSITE(primitive);
+
+ int primitive_n = filter->add_primitive(NR::NR_FILTER_COMPOSITE);
+ NR::FilterPrimitive *nr_primitive = filter->get_primitive(primitive_n);
+ NR::FilterComposite *nr_composite = dynamic_cast<NR::FilterComposite*>(nr_primitive);
+ g_assert(nr_composite != NULL);
+
+ sp_filter_primitive_renderer_common(primitive, nr_primitive);
+
+ nr_composite->set_operator(sp_composite->composite_operator);
+ nr_composite->set_input(1, sp_composite->in2);
+ if (sp_composite->composite_operator == COMPOSITE_ARITHMETIC) {
+ nr_composite->set_arithmetic(sp_composite->k1, sp_composite->k2,
+ sp_composite->k3, sp_composite->k4);
+ }
+}
+
/*
Local Variables:
diff --git a/src/sp-fecomposite.h b/src/sp-fecomposite.h
index 4421ebbf2..842495644 100644
--- a/src/sp-fecomposite.h
+++ b/src/sp-fecomposite.h
@@ -16,12 +16,25 @@
#include "sp-filter.h"
#include "sp-fecomposite-fns.h"
+enum FeCompositeOperator {
+ // Default value is 'over', but let's distinquish specifying the
+ // default and implicitely using the default
+ COMPOSITE_DEFAULT,
+ COMPOSITE_OVER,
+ COMPOSITE_IN,
+ COMPOSITE_OUT,
+ COMPOSITE_ATOP,
+ COMPOSITE_XOR,
+ COMPOSITE_ARITHMETIC
+};
+
/* FeComposite base class */
class SPFeCompositeClass;
struct SPFeComposite : public SPFilterPrimitive {
- /** COMPOSITE ATTRIBUTES HERE */
-
+ FeCompositeOperator composite_operator;
+ double k1, k2, k3, k4;
+ int in2;
};
struct SPFeCompositeClass {
diff --git a/src/sp-feoffset.cpp b/src/sp-feoffset.cpp
index b4a50679e..284846855 100644
--- a/src/sp-feoffset.cpp
+++ b/src/sp-feoffset.cpp
@@ -109,7 +109,8 @@ sp_feOffset_release(SPObject *object)
((SPObjectClass *) feOffset_parent_class)->release(object);
}
-double sp_feOffset_read_number(gchar const *value) {
+static double
+sp_feOffset_read_number(gchar const *value) {
char *end;
double ret = g_ascii_strtod(value, &end);
if (*end) {