From: kiirala Date: Mon, 9 Jul 2007 12:04:51 +0000 (+0000) Subject: First version of feComposite filter effect renderer X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=d0bb68525547101b5c938b246ff897a5de413cff;p=inkscape.git First version of feComposite filter effect renderer --- 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 -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(*out, *in1, *in2); + pixops_mix(*out, *in1, *in2); break; case BLEND_SCREEN: - _render(*out, *in1, *in2); + pixops_mix(*out, *in1, *in2); break; case BLEND_DARKEN: - _render(*out, *in1, *in2); + pixops_mix(*out, *in1, *in2); break; case BLEND_LIGHTEN: - _render(*out, *in1, *in2); + pixops_mix(*out, *in1, *in2); break; case BLEND_NORMAL: default: - _render(*out, *in1, *in2); + pixops_mix(*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 + * + * Copyright (C) 2007 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#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(*out, *in1, *in2); + break; + case COMPOSITE_OUT: + pixops_mix(*out, *in1, *in2); + break; + case COMPOSITE_ATOP: + pixops_mix(*out, *in1, *in2); + break; + case COMPOSITE_XOR: + pixops_mix(*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(*out, *in1, *in2); + break; + case COMPOSITE_DEFAULT: + case COMPOSITE_OVER: + default: + pixops_mix(*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 + * + * 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 + * + * 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 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_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) {