Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
nr-filter-convolve-matrix.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * feConvolveMatrix 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,2009 authors
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14#include <vector>
16#include "display/cairo-utils.h"
21
22namespace Inkscape {
23namespace Filters {
24
26
28
34
35template <PreserveAlphaMode preserve_alpha>
36struct ConvolveMatrix : public SurfaceSynth
37{
38 ConvolveMatrix(cairo_surface_t *s, int targetX, int targetY, int orderX, int orderY,
39 double divisor, double bias, std::vector<double> const &kernel)
40 : SurfaceSynth(s)
41 , _kernel(kernel.size())
42 , _targetX(targetX)
43 , _targetY(targetY)
44 , _orderX(orderX)
45 , _orderY(orderY)
46 , _bias(bias)
47 {
48 for (unsigned i = 0; i < _kernel.size(); ++i) {
49 _kernel[i] = kernel[i] / divisor;
50 }
51 // the matrix is given rotated 180 degrees
52 // which corresponds to reverse element order
53 std::reverse(_kernel.begin(), _kernel.end());
54 }
55
56 guint32 operator()(int x, int y) const
57 {
58 int startx = std::max(0, x - _targetX);
59 int starty = std::max(0, y - _targetY);
60 int endx = std::min(_w, startx + _orderX);
61 int endy = std::min(_h, starty + _orderY);
62 int limitx = endx - startx;
63 int limity = endy - starty;
64 double suma = 0.0, sumr = 0.0, sumg = 0.0, sumb = 0.0;
65
66 for (int i = 0; i < limity; ++i) {
67 for (int j = 0; j < limitx; ++j) {
68 guint32 px = pixelAt(startx + j, starty + i);
69 double coeff = _kernel[i * _orderX + j];
70 EXTRACT_ARGB32(px, a,r,g,b)
71
72 sumr += r * coeff;
73 sumg += g * coeff;
74 sumb += b * coeff;
75 if (preserve_alpha == NO_PRESERVE_ALPHA) {
76 suma += a * coeff;
77 }
78 }
79 }
80 if (preserve_alpha == PRESERVE_ALPHA) {
81 suma = alphaAt(x, y);
82 } else {
83 suma += _bias * 255;
84 }
85
86 guint32 ao = pxclamp(round(suma), 0, 255);
87 guint32 ro = pxclamp(round(sumr + ao * _bias), 0, ao);
88 guint32 go = pxclamp(round(sumg + ao * _bias), 0, ao);
89 guint32 bo = pxclamp(round(sumb + ao * _bias), 0, ao);
90 ASSEMBLE_ARGB32(pxout, ao,ro,go,bo);
91 return pxout;
92 }
93
94private:
95 std::vector<double> _kernel;
96 int _targetX, _targetY, _orderX, _orderY;
97 double _bias;
98};
99
101{
102 static bool bias_warning = false;
103 static bool edge_warning = false;
104
105 if (orderX<=0 || orderY<=0) {
106 g_warning("Empty kernel!");
107 return;
108 }
109 if (targetX<0 || targetX>=orderX || targetY<0 || targetY>=orderY) {
110 g_warning("Invalid target!");
111 return;
112 }
113 if (kernelMatrix.size()!=(unsigned int)(orderX*orderY)) {
114 //g_warning("kernelMatrix does not have orderX*orderY elements!");
115 return;
116 }
117
118 cairo_surface_t *input = slot.getcairo(_input);
120
121 // We may need to transform input surface to correct color interpolation space. The input surface
122 // might be used as input to another primitive but it is likely that all the primitives in a given
123 // filter use the same color interpolation space so we don't copy the input before converting.
126
127 if (bias != 0 && !bias_warning) {
128 g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix is correct!");
129 bias_warning = true;
130 // The SVG specification implies that feConvolveMatrix is defined for premultiplied
131 // colors (which makes sense). It also says that bias should simply be added to the result
132 // for each color (without taking the alpha into account). However, it also says that one
133 // purpose of bias is "to have .5 gray value be the zero response of the filter".
134 // It seems sensible to indeed support the latter behaviour instead of the former,
135 // but this does appear to go against the standard.
136 // Note that Batik simply does not support bias!=0
137 }
138 if (edgeMode != CONVOLVEMATRIX_EDGEMODE_NONE && !edge_warning) {
139 g_warning("Inkscape only supports edgeMode=\"none\" (and a filter uses a different one)!");
140 edge_warning = true;
141 }
142
143 //guint32 *in_data = reinterpret_cast<guint32*>(cairo_image_surface_get_data(input));
144 //guint32 *out_data = reinterpret_cast<guint32*>(cairo_image_surface_get_data(out));
145
146 //int width = cairo_image_surface_get_width(input);
147 //int height = cairo_image_surface_get_height(input);
148
149 // Set up predivided kernel matrix
150 /*std::vector<double> kernel(kernelMatrix);
151 for(size_t i=0; i<kernel.size(); i++) {
152 kernel[i] /= divisor; // The code that creates this object makes sure that divisor != 0
153 }*/
154
155 if (preserveAlpha) {
156 //convolve2D<true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY,
157 // targetX, targetY, bias);
158 ink_cairo_surface_synthesize(out, ConvolveMatrix<PRESERVE_ALPHA>(input,
160 } else {
161 //convolve2D<false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY,
162 // targetX, targetY, bias);
163 ink_cairo_surface_synthesize(out, ConvolveMatrix<NO_PRESERVE_ALPHA>(input,
165 }
166
167 slot.set(_output, out);
168 cairo_surface_destroy(out);
169}
170
172{
173 targetX = coord;
174}
175
177{
178 targetY = coord;
179}
180
182{
183 orderX = coord;
184}
185
187{
188 orderY = coord;
189}
190
192{
193 divisor = d;
194}
195
197{
198 bias = b;
199}
200
201void FilterConvolveMatrix::set_kernelMatrix(std::vector<gdouble> km)
202{
203 kernelMatrix = std::move(km);
204}
205
210
215
217{
218 //Seems to me that since this filter's operation is resolution dependent,
219 // some spurious pixels may still appear at the borders when low zooming or rotating. Needs a better fix.
220 area.setMin(area.min() - Geom::IntPoint(targetX, targetY));
221 // This makes sure the last row/column in the original image corresponds
222 // to the last row/column in the new image that can be convolved without
223 // adjusting the boundary conditions).
224 area.setMax(area.max() + Geom::IntPoint(orderX - targetX - 1, orderY - targetY - 1));
225}
226
228{
229 return kernelMatrix.size();
230}
231
232} // namespace Filters
233} // namespace Inkscape
234
235/*
236 Local Variables:
237 mode:c++
238 c-file-style:"stroustrup"
239 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
240 indent-tabs-mode:nil
241 fill-column:99
242 End:
243*/
244// 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_synthesize(cairo_surface_t *out, cairo_rectangle_t const &out_area, Synth &&synth)
Synthesize surface pixels based on their position.
cairo_surface_t * ink_cairo_surface_create_identical(cairo_surface_t *s)
Create a surface that differs only in pixel content.
void set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation ci)
Set the color_interpolation_value for a Cairo surface.
Cairo integration helpers.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Axis aligned, non-empty, generic rectangle.
void setMin(CPoint const &p)
Set the upper left point of the rectangle.
void setMax(CPoint const &p)
Set the lower right point of the rectangle.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint max() const
Get the corner of the rectangle with largest coordinate values.
Two-dimensional point with integer coordinates.
Definition int-point.h:57
void render_cairo(FilterSlot &slot) const override
double complexity(Geom::Affine const &ctm) const override
void set_edgeMode(FilterConvolveMatrixEdgeMode mode)
void area_enlarge(Geom::IntRect &area, Geom::Affine const &trans) const override
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
Helper class to stream background task notifications as a series of messages.
Definition of functions needed by several filters.
int mode
guint32 alphaAt(int x, int y) const
guint32 pixelAt(int x, int y) const