Code

Filter effects dialog:
[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 "isnan.h"
15 #include "sp-fecomposite.h"
16 #include "display/nr-filter-composite.h"
17 #include "display/nr-filter-pixops.h"
18 #include "display/nr-filter-slot.h"
19 #include "libnr/nr-blit.h"
20 #include "libnr/nr-pixblock.h"
21 #include "libnr/nr-pixops.h"
22 #include "libnr/nr-matrix.h"
24 inline void
25 composite_over(unsigned char *r, unsigned char const *a, unsigned char const *b)
26 {
27     r[0] = a[0] + NR_NORMALIZE_21(b[0] * (255 - a[3]));
28     r[1] = a[1] + NR_NORMALIZE_21(b[1] * (255 - a[3]));
29     r[2] = a[2] + NR_NORMALIZE_21(b[2] * (255 - a[3]));
30     r[3] = a[3] + NR_NORMALIZE_21(b[3] * (255 - a[3]));
31 }
33 inline void
34 composite_in(unsigned char *r, unsigned char const *a, unsigned char const *b)
35 {
36     r[0] = NR_NORMALIZE_21(a[0] * b[3]);
37     r[1] = NR_NORMALIZE_21(a[1] * b[3]);
38     r[2] = NR_NORMALIZE_21(a[2] * b[3]);
39     r[3] = NR_NORMALIZE_21(a[3] * b[3]);
40 }
42 inline void
43 composite_out(unsigned char *r, unsigned char const *a, unsigned char const *b)
44 {
45     r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3]));
46     r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3]));
47     r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3]));
48     r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3]));
49 }
51 inline void
52 composite_atop(unsigned char *r, unsigned char const *a, unsigned char const *b)
53 {
54     r[0] = NR_NORMALIZE_21(a[0] * b[3] + b[0] * (255 - a[3]));
55     r[1] = NR_NORMALIZE_21(a[1] * b[3] + b[1] * (255 - a[3]));
56     r[2] = NR_NORMALIZE_21(a[2] * b[3] + b[2] * (255 - a[3]));
57     r[3] = b[3];
58 }
60 inline void
61 composite_xor(unsigned char *r, unsigned char const *a, unsigned char const *b)
62 {
63     r[0] = NR_NORMALIZE_21(a[0] * (255 - b[3]) + b[0] * (255 - a[3]));
64     r[1] = NR_NORMALIZE_21(a[1] * (255 - b[3]) + b[1] * (255 - a[3]));
65     r[2] = NR_NORMALIZE_21(a[2] * (255 - b[3]) + b[2] * (255 - a[3]));
66     r[3] = NR_NORMALIZE_21(a[3] * (255 - b[3]) + b[3] * (255 - a[3]));
67 }
69 static int clamp(int val) {
70     if (val < 0) return 0;
71     if (val > 255) return 255;
72     return val;
73 }
75 // BUGBUG / TODO
76 // This makes arithmetic compositing non re-entrant and non thread safe.
77 static int arith_k1, arith_k2, arith_k3, arith_k4;
78 inline void
79 composite_arithmetic(unsigned char *r, unsigned char const *a, unsigned char const *b)
80 {
81     r[0] = clamp(NR_NORMALIZE_31(arith_k1 * a[0] * b[0]
82                  + arith_k2 * a[0] + arith_k3 * b[0] + arith_k4));
83     r[1] = clamp(NR_NORMALIZE_31(arith_k1 * a[1] * b[1]
84                  + arith_k2 * a[1] + arith_k3 * b[1] + arith_k4));
85     r[2] = clamp(NR_NORMALIZE_31(arith_k1 * a[2] * b[2]
86                  + arith_k2 * a[2] + arith_k3 * b[2] + arith_k4));
87     r[3] = clamp(NR_NORMALIZE_31(arith_k1 * a[3] * b[3]
88                  + arith_k2 * a[3] + arith_k3 * b[3] + arith_k4));
89 }
91 namespace NR {
93 FilterComposite::FilterComposite() :
94     op(COMPOSITE_DEFAULT), k1(0), k2(0), k3(0), k4(0),
95     _input2(NR::NR_FILTER_SLOT_NOT_SET)
96 {}
98 FilterPrimitive * FilterComposite::create() {
99     return new FilterComposite();
102 FilterComposite::~FilterComposite()
103 {}
105 int FilterComposite::render(FilterSlot &slot, Matrix const &trans) {
106     NRPixBlock *in1 = slot.get(_input);
107     NRPixBlock *in2 = slot.get(_input2);
108     NRPixBlock *original_in1 = in1;
109     NRPixBlock *original_in2 = in2;
110     NRPixBlock *out;
112     // Bail out if either one of source images is missing
113     if (!in1 || !in2) {
114         g_warning("Missing source image for feComposite (in=%d in2=%d)", _input, _input2);
115         return 1;
116     }
118     out = new NRPixBlock;
119     NRRectL out_area;
120     nr_rect_l_union(&out_area, &in1->area, &in2->area);
121     nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P,
122                            out_area.x0, out_area.y0, out_area.x1, out_area.y1,
123                            true);
125     // Blending modes are defined for premultiplied RGBA values,
126     // thus convert them to that format before blending
127     if (in1->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
128         in1 = new NRPixBlock;
129         nr_pixblock_setup_fast(in1, NR_PIXBLOCK_MODE_R8G8B8A8P,
130                                original_in1->area.x0, original_in1->area.y0,
131                                original_in1->area.x1, original_in1->area.y1,
132                                false);
133         nr_blit_pixblock_pixblock(in1, original_in1);
134     }
135     if (in2->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
136         in2 = new NRPixBlock;
137         nr_pixblock_setup_fast(in2, NR_PIXBLOCK_MODE_R8G8B8A8P,
138                                original_in2->area.x0, original_in2->area.y0,
139                                original_in2->area.x1, original_in2->area.y1,
140                                false);
141         nr_blit_pixblock_pixblock(in2, original_in2);
142     }
144     /* pixops_mix is defined in display/nr-filter-pixops.h
145      * It mixes the two input images with the function given as template
146      * and places the result in output image.
147      */
148     switch (op) {
149         case COMPOSITE_IN:
150             pixops_mix<composite_in>(*out, *in1, *in2);
151             break;
152         case COMPOSITE_OUT:
153             pixops_mix<composite_out>(*out, *in1, *in2);
154             break;
155         case COMPOSITE_ATOP:
156             pixops_mix<composite_atop>(*out, *in1, *in2);
157             break;
158         case COMPOSITE_XOR:
159             pixops_mix<composite_xor>(*out, *in1, *in2);
160             break;
161         case COMPOSITE_ARITHMETIC:
162             arith_k1 = (int)(k1 * 255);
163             arith_k2 = (int)(k2 * 255 * 255);
164             arith_k3 = (int)(k3 * 255 * 255);
165             arith_k4 = (int)(k4 * 255 * 255 * 255);
166             pixops_mix<composite_arithmetic>(*out, *in1, *in2);
167             break;
168         case COMPOSITE_DEFAULT:
169         case COMPOSITE_OVER:
170         default:
171             pixops_mix<composite_over>(*out, *in1, *in2);
172             break;
173     }
175     if (in1 != original_in1) {
176         nr_pixblock_release(in1);
177         delete in1;
178     }
179     if (in2 != original_in2) {
180         nr_pixblock_release(in2);
181         delete in2;
182     }
184     out->empty = FALSE;
185     slot.set(_output, out);
187     return 0;
190 void FilterComposite::set_input(int input) {
191     _input = input;
194 void FilterComposite::set_input(int input, int slot) {
195     if (input == 0) _input = slot;
196     if (input == 1) _input2 = slot;
199 void FilterComposite::set_operator(FeCompositeOperator op) {
200     if (op == COMPOSITE_DEFAULT) {
201         this->op = COMPOSITE_OVER;
202     } else if (op == COMPOSITE_OVER ||
203                op == COMPOSITE_IN ||
204                op == COMPOSITE_OUT ||
205                op == COMPOSITE_ATOP ||
206                op == COMPOSITE_XOR ||
207                op == COMPOSITE_ARITHMETIC)
208     {
209         this->op = op;
210     }
213 void FilterComposite::set_arithmetic(double k1, double k2, double k3, double k4) {
214     if (!isFinite(k1) || !isFinite(k2) || !isFinite(k3) || !isFinite(k4)) {
215         g_warning("Non-finite parameter for feComposite arithmetic operator");
216         return;
217     }
218     this->k1 = k1;
219     this->k2 = k2;
220     this->k3 = k3;
221     this->k4 = k4;
224 } /* namespace NR */
226 /*
227   Local Variables:
228   mode:c++
229   c-file-style:"stroustrup"
230   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
231   indent-tabs-mode:nil
232   fill-column:99
233   End:
234 */
235 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :