Code

First version of feComposite filter effect renderer
authorkiirala <kiirala@users.sourceforge.net>
Mon, 9 Jul 2007 12:04:51 +0000 (12:04 +0000)
committerkiirala <kiirala@users.sourceforge.net>
Mon, 9 Jul 2007 12:04:51 +0000 (12:04 +0000)
src/display/Makefile_insert
src/display/nr-filter-blend.cpp
src/display/nr-filter-composite.cpp [new file with mode: 0644]
src/display/nr-filter-composite.h [new file with mode: 0644]
src/display/nr-filter-pixops.h [new file with mode: 0644]
src/display/nr-filter-skeleton.cpp
src/display/nr-filter-skeleton.h
src/display/nr-filter.cpp
src/sp-fecomposite.cpp
src/sp-fecomposite.h
src/sp-feoffset.cpp

index 462da04c58f05d31a05fc7960a373e6fbbd1a697..1a4c01011af4648086e5c68d2363f8d87baa42e4 100644 (file)
@@ -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       \
index 0e2f965b7256cbe4ac0f0444cb929e4d184a1ac5..b02b20cbf6d8eddcfbaa2e97bf400d40d1865128 100644 (file)
@@ -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"
 #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 (file)
index 0000000..4358019
--- /dev/null
@@ -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 (file)
index 0000000..a1bbbb1
--- /dev/null
@@ -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 (file)
index 0000000..2baf342
--- /dev/null
@@ -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 :
index f595f13f16945fec949a1e9b35cb62911de419d8..68c38c4b22b5a0daac9c17fbf484ef71637129f3 100644 (file)
@@ -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)
index ae3a080468cb861ebd59d063ac4b78d937d2e88d..edba5c2f16834240590b3c6c6abdc8fb1a3912c6 100644 (file)
@@ -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)
index 33e89ac3eb3bede580cf4aa2cfc76f4ec2733dc9..61571f43bde80a5321bc764be81c6f532b253b45 100644 (file)
 #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;
index 86839aab5bfffea83a9329e3d0c941a3c253c757..68312aa1b9ffd95a046ad120d1e34683f23a263e 100644 (file)
@@ -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:
index 4421ebbf254e59ca1c19d952d4c8530fb9777da1..842495644ecd2426136c95348ffc471c9e637eb9 100644 (file)
 #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 {
index b4a50679ed18fcec7280c5df422c4ce3b35caca2..284846855e2c64277b0aeddeeb328b0380b3634b 100644 (file)
@@ -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) {