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