Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
nr-filter-component-transfer.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * feComponentTransfer filter primitive renderer
4 *
5 * Authors:
6 * Felipe CorrĂȘa da Silva Sanches <juca@members.fsf.org>
7 * Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
8 *
9 * Copyright (C) 2007 authors
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14#include <cmath>
16#include "display/cairo-utils.h"
19
20namespace Inkscape {
21namespace Filters {
22
24
26
27struct UnmultiplyAlpha
28{
29 guint32 operator()(guint32 in)
30 {
31 EXTRACT_ARGB32(in, a, r, g, b);
32 if (a == 0 )
33 return in;
34 r = unpremul_alpha(r, a);
35 g = unpremul_alpha(g, a);
36 b = unpremul_alpha(b, a);
37 ASSEMBLE_ARGB32(out, a, r, g, b);
38 return out;
39 }
40};
41
42struct MultiplyAlpha
43{
44 guint32 operator()(guint32 in)
45 {
46 EXTRACT_ARGB32(in, a, r, g, b);
47 r = premul_alpha(r, a);
48 g = premul_alpha(g, a);
49 b = premul_alpha(b, a);
50 ASSEMBLE_ARGB32(out, a, r, g, b);
51 return out;
52 }
53};
54
55struct ComponentTransfer
56{
57 ComponentTransfer(guint32 color)
58 : _shift(color * 8)
59 , _mask(0xff << _shift) {}
60
61protected:
62 guint32 _shift;
63 guint32 _mask;
64};
65
66struct ComponentTransferTable : public ComponentTransfer
67{
68 ComponentTransferTable(guint32 color, std::vector<double> const &values)
69 : ComponentTransfer(color)
70 , _v(values.size())
71 {
72 for (unsigned i = 0; i < values.size(); ++i) {
73 _v[i] = std::round(std::clamp(values[i], 0.0, 1.0) * 255);
74 }
75 }
76
77 guint32 operator()(guint32 in)
78 {
79 if (_v.empty()) {
80 return in;
81 }
82
83 guint32 component = (in & _mask) >> _shift;
84 if (_v.size() == 1 || component == 255) {
85 component = _v.back();
86 } else {
87 guint32 k = (_v.size() - 1) * component;
88 guint32 dx = k % 255;
89 k /= 255;
90 component = _v[k] * 255 + (_v[k + 1] - _v[k]) * dx;
91 component = (component + 127) / 255;
92 }
93 return (in & ~_mask) | (component << _shift);
94 }
95
96private:
97 std::vector<guint32> _v;
98};
99
100struct ComponentTransferDiscrete : public ComponentTransfer
101{
102 ComponentTransferDiscrete(guint32 color, std::vector<double> const &values)
103 : ComponentTransfer(color)
104 , _v(values.size())
105 {
106 for (unsigned i = 0; i< values.size(); ++i) {
107 _v[i] = std::round(std::clamp(values[i], 0.0, 1.0) * 255);
108 }
109 }
110
111 guint32 operator()(guint32 in)
112 {
113 guint32 component = (in & _mask) >> _shift;
114 guint32 k = _v.size() * component / 255;
115 if (k == _v.size()) --k;
116 component = _v[k];
117 return (in & ~_mask) | ((guint32)component << _shift);
118 }
119
120private:
121 std::vector<guint32> _v;
122};
123
124struct ComponentTransferLinear : public ComponentTransfer
125{
126 ComponentTransferLinear(guint32 color, double intercept, double slope)
127 : ComponentTransfer(color)
128 , _intercept(round(intercept * 255 * 255))
129 , _slope(round(slope * 255)) {}
130
131 guint32 operator()(guint32 in)
132 {
133 gint32 component = (in & _mask) >> _shift;
134
135 // TODO: this can probably be reduced to something simpler
136 component = pxclamp(_slope * component + _intercept, 0, 255 * 255);
137 component = (component + 127) / 255;
138 return (in & ~_mask) | (component << _shift);
139 }
140
141private:
142 gint32 _intercept;
143 gint32 _slope;
144};
145
146struct ComponentTransferGamma : public ComponentTransfer
147{
148 ComponentTransferGamma(guint32 color, double amplitude, double exponent, double offset)
149 : ComponentTransfer(color)
150 , _amplitude(amplitude)
151 , _exponent(exponent)
152 , _offset(offset) {}
153
154 guint32 operator()(guint32 in)
155 {
156 double component = (in & _mask) >> _shift;
157 component /= 255.0;
158 component = _amplitude * std::pow(component, _exponent) + _offset;
159 guint32 cpx = pxclamp(component * 255.0, 0, 255);
160 return (in & ~_mask) | (cpx << _shift);
161 }
162
163private:
164 double _amplitude;
165 double _exponent;
166 double _offset;
167};
168
170{
171 cairo_surface_t *input = slot.getcairo(_input);
172 cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA);
173
174 // We may need to transform input surface to correct color interpolation space. The input surface
175 // might be used as input to another primitive but it is likely that all the primitives in a given
176 // filter use the same color interpolation space so we don't copy the input before converting.
179
180 ink_cairo_surface_blit(input, out);
181
182 // We need to operate on unmultipled by alpha color values otherwise a change in alpha screws
183 // up the premultiplied by alpha r, g, b values.
184 ink_cairo_surface_filter(out, out, UnmultiplyAlpha());
185
186 // parameters: R = 0, G = 1, B = 2, A = 3
187 // Cairo: R = 2, G = 1, B = 0, A = 3
188 // If tableValues is empty, use identity.
189 for (unsigned i = 0; i < 4; ++i) {
190 guint32 color = 2 - i;
191 if (i == 3) color = 3; // alpha
192
193 switch (type[i]) {
195 if (!tableValues[i].empty()) {
196 ink_cairo_surface_filter(out, out, ComponentTransferTable(color, tableValues[i]));
197 }
198 break;
200 if (!tableValues[i].empty()) {
201 ink_cairo_surface_filter(out, out, ComponentTransferDiscrete(color, tableValues[i]));
202 }
203 break;
205 ink_cairo_surface_filter(out, out, ComponentTransferLinear(color, intercept[i], slope[i]));
206 break;
208 ink_cairo_surface_filter(out, out, ComponentTransferGamma(color, amplitude[i], exponent[i], offset[i]));
209 break;
212 default:
213 break;
214 }
215 }
216
217 ink_cairo_surface_filter(out, out, MultiplyAlpha());
218
219 slot.set(_output, out);
220 cairo_surface_destroy(out);
221}
222
224{
225 return true;
226}
227
229{
230 return 2.0;
231}
232
233} // namespace Filters
234} // namespace Inkscape
235
236/*
237 Local Variables:
238 mode:c++
239 c-file-style:"stroustrup"
240 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
241 indent-tabs-mode:nil
242 fill-column:99
243 End:
244*/
245// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Cairo software blending templates.
G_GNUC_CONST gint32 pxclamp(gint32 v, gint32 low, gint32 high)
void ink_cairo_surface_filter(cairo_surface_t *in, cairo_surface_t *out, Filter &&filter)
cairo_surface_t * ink_cairo_surface_create_same_size(cairo_surface_t *s, cairo_content_t c)
void ink_cairo_surface_blit(cairo_surface_t *src, cairo_surface_t *dest)
void set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation ci)
Set the color_interpolation_value for a Cairo surface.
Cairo integration helpers.
G_GNUC_CONST guint32 premul_alpha(const guint32 color, const guint32 alpha)
G_GNUC_CONST guint32 unpremul_alpha(const guint32 color, const guint32 alpha)
3x3 matrix representing an affine transformation.
Definition affine.h:70
double complexity(Geom::Affine const &ctm) const override
bool can_handle_affine(Geom::Affine const &) const override
Indicate whether the filter primitive can handle the given affine.
cairo_surface_t * getcairo(int slot)
Returns the pixblock in specified slot.
void set(int slot, cairo_surface_t *s)
Sets or re-sets the pixblock associated with given slot.
unsigned int guint32
struct _cairo_surface cairo_surface_t
double offset
Helper class to stream background task notifications as a series of messages.
signed int gint32