Code

Merge from trunk.
[inkscape.git] / src / display / nr-filter-composite.cpp
1 /*
2  * feComposite filter effect renderer
3  *
4  * Authors:
5  *   Niko Kiirala <niko@kiirala.com>
6  *
7  * Copyright (C) 2007 authors
8  *
9  * Released under GNU GPL, read the file 'COPYING' for more information
10  */
12 #include <cmath>
14 #include "2geom/isnan.h"
15 #include "filters/composite.h"
16 #include "display/nr-filter-composite.h"
17 #include "display/nr-filter-pixops.h"
18 #include "display/nr-filter-slot.h"
19 #include "display/nr-filter-units.h"
20 #include "display/nr-filter-utils.h"
21 #include "libnr/nr-blit.h"
22 #include "libnr/nr-pixblock.h"
23 #include "libnr/nr-pixops.h"
25 inline void
26 composite_over(unsigned char *r, unsigned char const *a, unsigned char const *b)
27 {
28     r[0] = NR_COMPOSEPPP_1111(a[0],a[3],b[0]);
29     r[1] = NR_COMPOSEPPP_1111(a[1],a[3],b[1]);
30     r[2] = NR_COMPOSEPPP_1111(a[2],a[3],b[2]);
31     r[3] = NR_COMPOSEPPP_1111(a[3],a[3],b[3]);
32 }
34 inline void
35 composite_in(unsigned char *r, unsigned char const *a, unsigned char const *b)
36 {
37     r[0] = NR_NORMALIZE_21(a[0] * b[3]);
38     r[1] = NR_NORMALIZE_21(a[1] * b[3]);
39     r[2] = NR_NORMALIZE_21(a[2] * b[3]);
40     r[3] = NR_NORMALIZE_21(a[3] * b[3]);
41 }
43 inline void
44 composite_out(unsigned char *r, unsigned char const *a, unsigned char const *b)
45 {
46     r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3]));
47     r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3]));
48     r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3]));
49     r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3]));
50 }
52 inline void
53 composite_atop(unsigned char *r, unsigned char const *a, unsigned char const *b)
54 {
55     r[0] = NR_NORMALIZE_21(a[0] * b[3] + b[0] * (255 - a[3]));
56     r[1] = NR_NORMALIZE_21(a[1] * b[3] + b[1] * (255 - a[3]));
57     r[2] = NR_NORMALIZE_21(a[2] * b[3] + b[2] * (255 - a[3]));
58     r[3] = b[3];
59 }
61 inline void
62 composite_xor(unsigned char *r, unsigned char const *a, unsigned char const *b)
63 {
64     r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3]) + b[0] * (255 - a[3]));
65     r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3]) + b[1] * (255 - a[3]));
66     r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3]) + b[2] * (255 - a[3]));
67     r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3]) + b[3] * (255 - a[3]));
68 }
70 // BUGBUG / TODO
71 // This makes arithmetic compositing non re-entrant and non thread safe.
72 static int arith_k1, arith_k2, arith_k3, arith_k4;
73 inline void
74 composite_arithmetic(unsigned char *r, unsigned char const *a, unsigned char const *b)
75 {
76     using Inkscape::Filters::clamp3;
77     r[0] = NR_NORMALIZE_31(clamp3(arith_k1 * a[0] * b[0]
78                  + arith_k2 * a[0] + arith_k3 * b[0] + arith_k4));
79     r[1] = NR_NORMALIZE_31(clamp3(arith_k1 * a[1] * b[1]
80                  + arith_k2 * a[1] + arith_k3 * b[1] + arith_k4));
81     r[2] = NR_NORMALIZE_31(clamp3(arith_k1 * a[2] * b[2]
82                  + arith_k2 * a[2] + arith_k3 * b[2] + arith_k4));
83     r[3] = NR_NORMALIZE_31(clamp3(arith_k1 * a[3] * b[3]
84                  + arith_k2 * a[3] + arith_k3 * b[3] + arith_k4));
85 }
87 namespace Inkscape {
88 namespace Filters {
90 FilterComposite::FilterComposite() :
91     op(COMPOSITE_DEFAULT), k1(0), k2(0), k3(0), k4(0),
92     _input2(Inkscape::Filters::NR_FILTER_SLOT_NOT_SET)
93 {}
95 FilterPrimitive * FilterComposite::create() {
96     return new FilterComposite();
97 }
99 FilterComposite::~FilterComposite()
100 {}
102 int FilterComposite::render(FilterSlot &slot, FilterUnits const &/*units*/) {
103     NRPixBlock *in1 = slot.get(_input);
104     NRPixBlock *in2 = slot.get(_input2);
105     NRPixBlock *original_in1 = in1;
106     NRPixBlock *original_in2 = in2;
107     NRPixBlock *out;
109     // Bail out if either one of source images is missing
110     if (!in1 || !in2) {
111         g_warning("Missing source image for feComposite (in=%d in2=%d)", _input, _input2);
112         return 1;
113     }
115     out = new NRPixBlock;
116     NRRectL out_area;
117     nr_rect_l_union(&out_area, &in1->area, &in2->area);
118     nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P,
119                            out_area.x0, out_area.y0, out_area.x1, out_area.y1,
120                            true);
122     // Blending modes are defined for premultiplied RGBA values,
123     // thus convert them to that format before blending
124     if (in1->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
125         in1 = nr_pixblock_new_fast(NR_PIXBLOCK_MODE_R8G8B8A8P,
126                                    original_in1->area.x0, original_in1->area.y0,
127                                    original_in1->area.x1, original_in1->area.y1,
128                                    false);
129         nr_blit_pixblock_pixblock(in1, original_in1);
130     }
131     if (in2->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
132         in2 = nr_pixblock_new_fast(NR_PIXBLOCK_MODE_R8G8B8A8P,
133                                    original_in2->area.x0, original_in2->area.y0,
134                                    original_in2->area.x1, original_in2->area.y1,
135                                    false);
136         nr_blit_pixblock_pixblock(in2, original_in2);
137     }
139     /* pixops_mix is defined in display/nr-filter-pixops.h
140      * It mixes the two input images with the function given as template
141      * and places the result in output image.
142      */
143     switch (op) {
144         case COMPOSITE_IN:
145             pixops_mix<composite_in>(*out, *in1, *in2);
146             break;
147         case COMPOSITE_OUT:
148             pixops_mix<composite_out>(*out, *in1, *in2);
149             break;
150         case COMPOSITE_ATOP:
151             pixops_mix<composite_atop>(*out, *in1, *in2);
152             break;
153         case COMPOSITE_XOR:
154             pixops_mix<composite_xor>(*out, *in1, *in2);
155             break;
156         case COMPOSITE_ARITHMETIC:
157             arith_k1 = (int)round(k1 * 255);
158             arith_k2 = (int)round(k2 * 255 * 255);
159             arith_k3 = (int)round(k3 * 255 * 255);
160             arith_k4 = (int)round(k4 * 255 * 255 * 255);
161             pixops_mix<composite_arithmetic>(*out, *in1, *in2);
162             break;
163         case COMPOSITE_DEFAULT:
164         case COMPOSITE_OVER:
165         default:
166             pixops_mix<composite_over>(*out, *in1, *in2);
167             break;
168     }
170     if (in1 != original_in1) {
171         nr_pixblock_free(in1);
172     }
173     if (in2 != original_in2) {
174         nr_pixblock_free(in2);
175     }
177     out->empty = FALSE;
178     slot.set(_output, out);
180     return 0;
183 void FilterComposite::set_input(int input) {
184     _input = input;
187 void FilterComposite::set_input(int input, int slot) {
188     if (input == 0) _input = slot;
189     if (input == 1) _input2 = slot;
192 void FilterComposite::set_operator(FeCompositeOperator op) {
193     if (op == COMPOSITE_DEFAULT) {
194         this->op = COMPOSITE_OVER;
195     } else if (op == COMPOSITE_OVER ||
196                op == COMPOSITE_IN ||
197                op == COMPOSITE_OUT ||
198                op == COMPOSITE_ATOP ||
199                op == COMPOSITE_XOR ||
200                op == COMPOSITE_ARITHMETIC)
201     {
202         this->op = op;
203     }
206 void FilterComposite::set_arithmetic(double k1, double k2, double k3, double k4) {
207     if (!IS_FINITE(k1) || !IS_FINITE(k2) || !IS_FINITE(k3) || !IS_FINITE(k4)) {
208         g_warning("Non-finite parameter for feComposite arithmetic operator");
209         return;
210     }
211     this->k1 = k1;
212     this->k2 = k2;
213     this->k3 = k3;
214     this->k4 = k4;
217 } /* namespace Filters */
218 } /* namespace Inkscape */
220 /*
221   Local Variables:
222   mode:c++
223   c-file-style:"stroustrup"
224   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
225   indent-tabs-mode:nil
226   fill-column:99
227   End:
228 */
229 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :