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
10
* Niko Kiirala <niko@kiirala.com>
12
* Copyright (C) 2007 authors
14
* Released under GNU GPL, read the file 'COPYING' for more information
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 "display/nr-filter-units.h"
23
#include "libnr/nr-pixblock.h"
24
#include "libnr/nr-matrix.h"
25
#include "libnr/nr-blit.h"
26
#include "libnr/nr-pixops.h"
31
* From http://www.w3.org/TR/SVG11/filters.html#feBlend
33
* For all feBlend modes, the result opacity is computed as follows:
34
* qr = 1 - (1-qa)*(1-qb)
36
* For the compositing formulas below, the following definitions apply:
37
* cr = Result color (RGB) - premultiplied
38
* qa = Opacity value at a given pixel for image A
39
* qb = Opacity value at a given pixel for image B
40
* ca = Color (RGB) at a given pixel for image A - premultiplied
41
* cb = Color (RGB) at a given pixel for image B - premultiplied
45
* These blending equations given in SVG standard are for color values
46
* in the range 0..1. As these values are stored as unsigned char values,
47
* they need some reworking. An unsigned char value can be thought as
48
* 0.8 fixed point representation of color value. This is how I've
49
* ended up with these equations here.
52
// Set alpha / opacity. This line is same for all the blending modes,
53
// so let's save some copy-pasting.
54
#define SET_ALPHA r[3] = NR_NORMALIZE_21((255 * 255) - (255 - a[3]) * (255 - b[3]))
56
// cr = (1 - qa) * cb + ca
58
blend_normal(unsigned char *r, unsigned char const *a, unsigned char const *b)
60
r[0] = NR_NORMALIZE_21((255 - a[3]) * b[0]) + a[0];
61
r[1] = NR_NORMALIZE_21((255 - a[3]) * b[1]) + a[1];
62
r[2] = NR_NORMALIZE_21((255 - a[3]) * b[2]) + a[2];
66
// cr = (1-qa)*cb + (1-qb)*ca + ca*cb
68
blend_multiply(unsigned char *r, unsigned char const *a, unsigned char const *b)
70
r[0] = NR_NORMALIZE_21((255 - a[3]) * b[0] + (255 - b[3]) * a[0]
72
r[1] = NR_NORMALIZE_21((255 - a[3]) * b[1] + (255 - b[3]) * a[1]
74
r[2] = NR_NORMALIZE_21((255 - a[3]) * b[2] + (255 - b[3]) * a[2]
79
// cr = cb + ca - ca * cb
81
blend_screen(unsigned char *r, unsigned char const *a, unsigned char const *b)
83
r[0] = NR_NORMALIZE_21(b[0] * 255 + a[0] * 255 - a[0] * b[0]);
84
r[1] = NR_NORMALIZE_21(b[1] * 255 + a[1] * 255 - a[1] * b[1]);
85
r[2] = NR_NORMALIZE_21(b[2] * 255 + a[2] * 255 - a[2] * b[2]);
89
// cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb)
91
blend_darken(unsigned char *r, unsigned char const *a, unsigned char const *b)
93
r[0] = std::min(NR_NORMALIZE_21((255 - a[3]) * b[0]) + a[0],
94
NR_NORMALIZE_21((255 - b[3]) * a[0]) + b[0]);
95
r[1] = std::min(NR_NORMALIZE_21((255 - a[3]) * b[1]) + a[1],
96
NR_NORMALIZE_21((255 - b[3]) * a[1]) + b[1]);
97
r[2] = std::min(NR_NORMALIZE_21((255 - a[3]) * b[2]) + a[2],
98
NR_NORMALIZE_21((255 - b[3]) * a[2]) + b[2]);
102
// cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb)
104
blend_lighten(unsigned char *r, unsigned char const *a, unsigned char const *b)
106
r[0] = std::max(NR_NORMALIZE_21((255 - a[3]) * b[0]) + a[0],
107
NR_NORMALIZE_21((255 - b[3]) * a[0]) + b[0]);
108
r[1] = std::max(NR_NORMALIZE_21((255 - a[3]) * b[1]) + a[1],
109
NR_NORMALIZE_21((255 - b[3]) * a[1]) + b[1]);
110
r[2] = std::max(NR_NORMALIZE_21((255 - a[3]) * b[2]) + a[2],
111
NR_NORMALIZE_21((255 - b[3]) * a[2]) + b[2]);
115
FilterBlend::FilterBlend()
116
: _blend_mode(BLEND_NORMAL),
117
_input2(NR_FILTER_SLOT_NOT_SET)
120
FilterPrimitive * FilterBlend::create() {
121
return new FilterBlend();
124
FilterBlend::~FilterBlend()
127
int FilterBlend::render(FilterSlot &slot, FilterUnits const & /*units*/) {
128
NRPixBlock *in1 = slot.get(_input);
129
NRPixBlock *in2 = slot.get(_input2);
130
NRPixBlock *original_in1 = in1;
131
NRPixBlock *original_in2 = in2;
134
// Bail out if either one of source images is missing
136
g_warning("Missing source image for feBlend (in=%d in2=%d)", _input, _input2);
140
out = new NRPixBlock;
142
nr_rect_l_union(&out_area, &in1->area, &in2->area);
143
nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8P,
144
out_area.x0, out_area.y0, out_area.x1, out_area.y1,
147
// Blending modes are defined for premultiplied RGBA values,
148
// thus convert them to that format before blending
149
if (in1->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
150
in1 = new NRPixBlock;
151
nr_pixblock_setup_fast(in1, NR_PIXBLOCK_MODE_R8G8B8A8P,
152
original_in1->area.x0, original_in1->area.y0,
153
original_in1->area.x1, original_in1->area.y1,
155
nr_blit_pixblock_pixblock(in1, original_in1);
157
if (in2->mode != NR_PIXBLOCK_MODE_R8G8B8A8P) {
158
in2 = new NRPixBlock;
159
nr_pixblock_setup_fast(in2, NR_PIXBLOCK_MODE_R8G8B8A8P,
160
original_in2->area.x0, original_in2->area.y0,
161
original_in2->area.x1, original_in2->area.y1,
163
nr_blit_pixblock_pixblock(in2, original_in2);
166
/* pixops_mix is defined in display/nr-filter-pixops.h
167
* It mixes the two input images with the function given as template
168
* and places the result in output image.
170
switch (_blend_mode) {
172
pixops_mix<blend_multiply>(*out, *in1, *in2);
175
pixops_mix<blend_screen>(*out, *in1, *in2);
178
pixops_mix<blend_darken>(*out, *in1, *in2);
181
pixops_mix<blend_lighten>(*out, *in1, *in2);
185
pixops_mix<blend_normal>(*out, *in1, *in2);
189
if (in1 != original_in1) {
190
nr_pixblock_release(in1);
193
if (in2 != original_in2) {
194
nr_pixblock_release(in2);
199
slot.set(_output, out);
204
void FilterBlend::set_input(int slot) {
208
void FilterBlend::set_input(int input, int slot) {
209
if (input == 0) _input = slot;
210
if (input == 1) _input2 = slot;
213
void FilterBlend::set_mode(FilterBlendMode mode) {
214
if (mode == BLEND_NORMAL || mode == BLEND_MULTIPLY ||
215
mode == BLEND_SCREEN || mode == BLEND_DARKEN ||
216
mode == BLEND_LIGHTEN)
227
c-file-style:"stroustrup"
228
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
233
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :