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)(k1 * 255);
158 arith_k2 = (int)(k2 * 255 * 255);
159 arith_k3 = (int)(k3 * 255 * 255);
160 arith_k4 = (int)(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;
181 }
183 void FilterComposite::set_input(int input) {
184 _input = input;
185 }
187 void FilterComposite::set_input(int input, int slot) {
188 if (input == 0) _input = slot;
189 if (input == 1) _input2 = slot;
190 }
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 }
204 }
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;
215 }
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:encoding=utf-8:textwidth=99 :