Code

b02b20cbf6d8eddcfbaa2e97bf400d40d1865128
[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  *
12  * Copyright (C) 2007 authors
13  *
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 #include "display/nr-filter-blend.h"
18 #include "display/nr-filter-pixops.h"
19 #include "display/nr-filter-primitive.h"
20 #include "display/nr-filter-slot.h"
21 #include "display/nr-filter-types.h"
22 #include "libnr/nr-pixblock.h"
23 #include "libnr/nr-matrix.h"
24 #include "libnr/nr-blit.h"
25 #include "libnr/nr-pixops.h"
27 namespace NR {
29 /*
30  * From http://www.w3.org/TR/SVG11/filters.html#feBlend
31  *
32  * For all feBlend modes, the result opacity is computed as follows:
33  * qr = 1 - (1-qa)*(1-qb)
34  *
35  * For the compositing formulas below, the following definitions apply:
36  * cr = Result color (RGB) - premultiplied
37  * qa = Opacity value at a given pixel for image A
38  * qb = Opacity value at a given pixel for image B
39  * ca = Color (RGB) at a given pixel for image A - premultiplied
40  * cb = Color (RGB) at a given pixel for image B - premultiplied
41  */
43 /*
44  * These blending equations given in SVG standard are for color values
45  * in the range 0..1. As these values are stored as unsigned char values,
46  * they need some reworking. An unsigned char value can be thought as
47  * 0.8 fixed point representation of color value. This is how I've
48  * ended up with these equations here.
49  */
51 // Set alpha / opacity. This line is same for all the blending modes,
52 // so let's save some copy-pasting.
53 #define SET_ALPHA r[3] = NR_NORMALIZE_21((255 * 255) - (255 - a[3]) * (255 - b[3]))
55 // cr = (1 - qa) * cb + ca
56 inline void
57 blend_normal(unsigned char *r, unsigned char const *a, unsigned char const *b)
58 {
59     r[0] = NR_NORMALIZE_21((255 - a[3]) * b[0]) + a[0];
60     r[1] = NR_NORMALIZE_21((255 - a[3]) * b[1]) + a[1];
61     r[2] = NR_NORMALIZE_21((255 - a[3]) * b[2]) + a[2];
62     SET_ALPHA;
63 }
65 // cr = (1-qa)*cb + (1-qb)*ca + ca*cb
66 inline void
67 blend_multiply(unsigned char *r, unsigned char const *a, unsigned char const *b)
68 {
69     r[0] = NR_NORMALIZE_21((255 - a[3]) * b[0] + (255 - b[3]) * a[0]
70                            + a[0] * b[0]);
71     r[1] = NR_NORMALIZE_21((255 - a[3]) * b[1] + (255 - b[3]) * a[1]
72                            + a[1] * b[1]);
73     r[2] = NR_NORMALIZE_21((255 - a[3]) * b[2] + (255 - b[3]) * a[2]
74                            + a[2] * b[2]);
75     SET_ALPHA;
76 }
78 // cr = cb + ca - ca * cb
79 inline void
80 blend_screen(unsigned char *r, unsigned char const *a, unsigned char const *b)
81 {
82     r[0] = NR_NORMALIZE_21(b[0] * 255 + a[0] * 255 - a[0] * b[0]);
83     r[1] = NR_NORMALIZE_21(b[1] * 255 + a[1] * 255 - a[1] * b[1]);
84     r[2] = NR_NORMALIZE_21(b[2] * 255 + a[2] * 255 - a[2] * b[2]);
85     SET_ALPHA;
86 }
88 // cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb)
89 inline void
90 blend_darken(unsigned char *r, unsigned char const *a, unsigned char const *b)
91 {
92     r[0] = std::min(NR_NORMALIZE_21((255 - a[3]) * b[0]) + a[0],
93                     NR_NORMALIZE_21((255 - b[3]) * a[0]) + b[0]);
94     r[1] = std::min(NR_NORMALIZE_21((255 - a[3]) * b[1]) + a[1],
95                     NR_NORMALIZE_21((255 - b[3]) * a[1]) + b[1]);
96     r[2] = std::min(NR_NORMALIZE_21((255 - a[3]) * b[2]) + a[2],
97                     NR_NORMALIZE_21((255 - b[3]) * a[2]) + b[2]);
98     SET_ALPHA;
99 }
101 // cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb)
102 inline void
103 blend_lighten(unsigned char *r, unsigned char const *a, unsigned char const *b)
105     r[0] = std::max(NR_NORMALIZE_21((255 - a[3]) * b[0]) + a[0],
106                     NR_NORMALIZE_21((255 - b[3]) * a[0]) + b[0]);
107     r[1] = std::max(NR_NORMALIZE_21((255 - a[3]) * b[1]) + a[1],
108                     NR_NORMALIZE_21((255 - b[3]) * a[1]) + b[1]);
109     r[2] = std::max(NR_NORMALIZE_21((255 - a[3]) * b[2]) + a[2],
110                     NR_NORMALIZE_21((255 - b[3]) * a[2]) + b[2]);
111     SET_ALPHA;
114 FilterBlend::FilterBlend() 
115     : _blend_mode(BLEND_NORMAL),
116       _input2(NR_FILTER_SLOT_NOT_SET)
117 {}
119 FilterPrimitive * FilterBlend::create() {
120     return new FilterBlend();
123 FilterBlend::~FilterBlend()
124 {}
126 int FilterBlend::render(FilterSlot &slot, Matrix const &trans) {
127     NRPixBlock *in1 = slot.get(_input);
128     NRPixBlock *in2 = slot.get(_input2);
129     NRPixBlock *original_in1 = in1;
130     NRPixBlock *original_in2 = in2;
131     NRPixBlock *out;
133     // Bail out if either one of source images is missing
134     if (!in1 || !in2) {
135         g_warning("Missing source image for feBlend (in=%d in2=%d)", _input, _input2);
136         return 1;
137     }
139     out = new NRPixBlock;
140     NRRectL out_area;
141     nr_rect_l_union(&out_area, &in1->area, &in2->area);
142     nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P,
143                            out_area.x0, out_area.y0, out_area.x1, out_area.y1,
144                            true);
146     // Blending modes are defined for premultiplied RGBA values,
147     // thus convert them to that format before blending
148     if (in1->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
149         in1 = new NRPixBlock;
150         nr_pixblock_setup_fast(in1, NR_PIXBLOCK_MODE_R8G8B8A8P,
151                                original_in1->area.x0, original_in1->area.y0,
152                                original_in1->area.x1, original_in1->area.y1,
153                                false);
154         nr_blit_pixblock_pixblock(in1, original_in1);
155     }
156     if (in2->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
157         in2 = new NRPixBlock;
158         nr_pixblock_setup_fast(in2, NR_PIXBLOCK_MODE_R8G8B8A8P,
159                                original_in2->area.x0, original_in2->area.y0,
160                                original_in2->area.x1, original_in2->area.y1,
161                                false);
162         nr_blit_pixblock_pixblock(in2, original_in2);
163     }
165     /* pixops_mix is defined in display/nr-filter-pixops.h
166      * It mixes the two input images with the function given as template
167      * and places the result in output image.
168      */
169     switch (_blend_mode) {
170         case BLEND_MULTIPLY:
171             pixops_mix<blend_multiply>(*out, *in1, *in2);
172             break;
173         case BLEND_SCREEN:
174             pixops_mix<blend_screen>(*out, *in1, *in2);
175             break;
176         case BLEND_DARKEN:
177             pixops_mix<blend_darken>(*out, *in1, *in2);
178             break;
179         case BLEND_LIGHTEN:
180             pixops_mix<blend_lighten>(*out, *in1, *in2);
181             break;
182         case BLEND_NORMAL:
183         default:
184             pixops_mix<blend_normal>(*out, *in1, *in2);
185             break;
186     }
188     if (in1 != original_in1) {
189         nr_pixblock_release(in1);
190         delete in1;
191     }
192     if (in2 != original_in2) {
193         nr_pixblock_release(in2);
194         delete in2;
195     }
197     out->empty = FALSE;
198     slot.set(_output, out);
200     return 0;
203 void FilterBlend::set_input(int slot) {
204     _input = slot;
207 void FilterBlend::set_input(int input, int slot) {
208     if (input == 0) _input = slot;
209     if (input == 1) _input2 = slot;
212 void FilterBlend::set_mode(FilterBlendMode mode) {
213     if (mode == BLEND_NORMAL || mode == BLEND_MULTIPLY ||
214         mode == BLEND_SCREEN || mode == BLEND_DARKEN ||
215         mode == BLEND_LIGHTEN)
216     {
217         _blend_mode = mode;
218     }
221 } /* namespace NR */
223 /*
224   Local Variables:
225   mode:c++
226   c-file-style:"stroustrup"
227   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
228   indent-tabs-mode:nil
229   fill-column:99
230   End:
231 */
232 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :