Code

4645d9bc063a8bf0493e202e0b54ff4f5f0365b3
[inkscape.git] / src / display / nr-filter-blend.cpp
1 /*
2  * SVG feBlend renderer
3  *
4  * "This filter composites two objects together using commonly used
5  * imaging software blending modes. It performs a pixel-wise combination
6  * of two input images." 
7  * http://www.w3.org/TR/SVG11/filters.html#feBlend
8  *
9  * Authors:
10  *   Niko Kiirala <niko@kiirala.com>
11  *   Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
12  *
13  * Copyright (C) 2007-2008 authors
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #include "display/nr-filter-blend.h"
19 #include "display/nr-filter-pixops.h"
20 #include "display/nr-filter-primitive.h"
21 #include "display/nr-filter-slot.h"
22 #include "display/nr-filter-types.h"
23 #include "display/nr-filter-units.h"
24 #include "libnr/nr-pixblock.h"
25 #include "libnr/nr-blit.h"
26 #include "libnr/nr-pixops.h"
28 namespace Inkscape {
29 namespace Filters {
31 /*
32  * From http://www.w3.org/TR/SVG11/filters.html#feBlend
33  *
34  * For all feBlend modes, the result opacity is computed as follows:
35  * qr = 1 - (1-qa)*(1-qb)
36  *
37  * For the compositing formulas below, the following definitions apply:
38  * cr = Result color (RGB) - premultiplied
39  * qa = Opacity value at a given pixel for image A
40  * qb = Opacity value at a given pixel for image B
41  * ca = Color (RGB) at a given pixel for image A - premultiplied
42  * cb = Color (RGB) at a given pixel for image B - premultiplied
43  */
45 /*
46  * These blending equations given in SVG standard are for color values
47  * in the range 0..1. As these values are stored as unsigned char values,
48  * they need some reworking. An unsigned char value can be thought as
49  * 0.8 fixed point representation of color value. This is how I've
50  * ended up with these equations here.
51  */
53 // Set alpha / opacity. This line is same for all the blending modes,
54 // so let's save some copy-pasting.
55 #define SET_ALPHA r[3] = NR_NORMALIZE_21((255 * 255) - (255 - a[3]) * (255 - b[3]))
57 // cr = (1 - qa) * cb + ca
58 inline void
59 blend_normal(unsigned char *r, unsigned char const *a, unsigned char const *b)
60 {
61     r[0] = NR_COMPOSEPPP_1111(a[0],a[3],b[0]);
62     r[1] = NR_COMPOSEPPP_1111(a[1],a[3],b[1]);
63     r[2] = NR_COMPOSEPPP_1111(a[2],a[3],b[2]);
64     SET_ALPHA;
65 }
67 // cr = (1-qa)*cb + (1-qb)*ca + ca*cb
68 inline void
69 blend_multiply(unsigned char *r, unsigned char const *a, unsigned char const *b)
70 {
71     r[0] = NR_NORMALIZE_21((255 - a[3]) * b[0] + (255 - b[3]) * a[0]
72                            + a[0] * b[0]);
73     r[1] = NR_NORMALIZE_21((255 - a[3]) * b[1] + (255 - b[3]) * a[1]
74                            + a[1] * b[1]);
75     r[2] = NR_NORMALIZE_21((255 - a[3]) * b[2] + (255 - b[3]) * a[2]
76                            + a[2] * b[2]);
77     SET_ALPHA;
78 }
80 // cr = cb + ca - ca * cb
81 inline void
82 blend_screen(unsigned char *r, unsigned char const *a, unsigned char const *b)
83 {
84     r[0] = NR_NORMALIZE_21((b[0] + a[0]) * 255 - a[0] * b[0]);
85     r[1] = NR_NORMALIZE_21((b[1] + a[1]) * 255 - a[1] * b[1]);
86     r[2] = NR_NORMALIZE_21((b[2] + a[2]) * 255 - a[2] * b[2]);
87     SET_ALPHA;
88 }
90 // cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb)
91 inline void
92 blend_darken(unsigned char *r, unsigned char const *a, unsigned char const *b)
93 {
94     r[0] = NR_NORMALIZE_21(std::min(NR_COMPOSEPPP_1112(a[0],a[3],b[0]),
95                                     NR_COMPOSEPPP_1112(b[0],b[3],a[0])));
96     r[1] = NR_NORMALIZE_21(std::min(NR_COMPOSEPPP_1112(a[1],a[3],b[1]),
97                                     NR_COMPOSEPPP_1112(b[1],b[3],a[1])));
98     r[2] = NR_NORMALIZE_21(std::min(NR_COMPOSEPPP_1112(a[2],a[3],b[2]),
99                                     NR_COMPOSEPPP_1112(b[2],b[3],a[2])));
100     SET_ALPHA;
103 // cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb)
104 inline void
105 blend_lighten(unsigned char *r, unsigned char const *a, unsigned char const *b)
107     r[0] = NR_NORMALIZE_21(std::max(NR_COMPOSEPPP_1112(a[0],a[3],b[0]),
108                                     NR_COMPOSEPPP_1112(b[0],b[3],a[0])));
109     r[1] = NR_NORMALIZE_21(std::max(NR_COMPOSEPPP_1112(a[1],a[3],b[1]),
110                                     NR_COMPOSEPPP_1112(b[1],b[3],a[1])));
111     r[2] = NR_NORMALIZE_21(std::max(NR_COMPOSEPPP_1112(a[2],a[3],b[2]),
112                                     NR_COMPOSEPPP_1112(b[2],b[3],a[2])));
113     SET_ALPHA;
116 FilterBlend::FilterBlend() 
117     : _blend_mode(BLEND_NORMAL),
118       _input2(NR_FILTER_SLOT_NOT_SET)
119 {}
121 FilterPrimitive * FilterBlend::create() {
122     return new FilterBlend();
125 FilterBlend::~FilterBlend()
126 {}
128 int FilterBlend::render(FilterSlot &slot, FilterUnits const & /*units*/) {
129     NRPixBlock *in1 = slot.get(_input);
130     NRPixBlock *in2 = slot.get(_input2);
131     NRPixBlock *original_in1 = in1;
132     NRPixBlock *original_in2 = in2;
133     NRPixBlock *out;
135     // Bail out if either one of source images is missing
136     if (!in1 || !in2) {
137         g_warning("Missing source image for feBlend (in=%d in2=%d)", _input, _input2);
138         return 1;
139     }
141     out = new NRPixBlock;
142     NRRectL out_area;
143     nr_rect_l_union(&out_area, &in1->area, &in2->area);
144     nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P,
145                            out_area.x0, out_area.y0, out_area.x1, out_area.y1,
146                            true);
148     // Blending modes are defined for premultiplied RGBA values,
149     // thus convert them to that format before blending
150     if (in1->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
151         in1 = new NRPixBlock;
152         nr_pixblock_setup_fast(in1, NR_PIXBLOCK_MODE_R8G8B8A8P,
153                                original_in1->area.x0, original_in1->area.y0,
154                                original_in1->area.x1, original_in1->area.y1,
155                                false);
156         nr_blit_pixblock_pixblock(in1, original_in1);
157     }
158     if (in2->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
159         in2 = new NRPixBlock;
160         nr_pixblock_setup_fast(in2, NR_PIXBLOCK_MODE_R8G8B8A8P,
161                                original_in2->area.x0, original_in2->area.y0,
162                                original_in2->area.x1, original_in2->area.y1,
163                                false);
164         nr_blit_pixblock_pixblock(in2, original_in2);
165     }
167     /* pixops_mix is defined in display/nr-filter-pixops.h
168      * It mixes the two input images with the function given as template
169      * and places the result in output image.
170      */
171     switch (_blend_mode) {
172         case BLEND_MULTIPLY:
173             pixops_mix<blend_multiply>(*out, *in1, *in2);
174             break;
175         case BLEND_SCREEN:
176             pixops_mix<blend_screen>(*out, *in1, *in2);
177             break;
178         case BLEND_DARKEN:
179             pixops_mix<blend_darken>(*out, *in1, *in2);
180             break;
181         case BLEND_LIGHTEN:
182             pixops_mix<blend_lighten>(*out, *in1, *in2);
183             break;
184         case BLEND_NORMAL:
185         default:
186             pixops_mix<blend_normal>(*out, *in1, *in2);
187             break;
188     }
190     if (in1 != original_in1) {
191         nr_pixblock_release(in1);
192         delete in1;
193     }
194     if (in2 != original_in2) {
195         nr_pixblock_release(in2);
196         delete in2;
197     }
199     out->empty = FALSE;
200     slot.set(_output, out);
202     return 0;
205 void FilterBlend::set_input(int slot) {
206     _input = slot;
209 void FilterBlend::set_input(int input, int slot) {
210     if (input == 0) _input = slot;
211     if (input == 1) _input2 = slot;
214 void FilterBlend::set_mode(FilterBlendMode mode) {
215     if (mode == BLEND_NORMAL || mode == BLEND_MULTIPLY ||
216         mode == BLEND_SCREEN || mode == BLEND_DARKEN ||
217         mode == BLEND_LIGHTEN)
218     {
219         _blend_mode = mode;
220     }
223 } /* namespace Filters */
224 } /* namespace Inkscape */
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 :