1 /*
2 * feConvolveMatrix filter primitive renderer
3 *
4 * Authors:
5 * Felipe CorrĂȘa da Silva Sanches <felipe.sanches@gmail.com>
6 *
7 * Copyright (C) 2007 authors
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
12 #include "display/nr-filter-convolve-matrix.h"
13 #include "display/nr-filter-units.h"
14 #include "display/nr-filter-utils.h"
15 #include <vector>
17 namespace Inkscape {
18 namespace Filters {
20 FilterConvolveMatrix::FilterConvolveMatrix()
21 {}
23 FilterPrimitive * FilterConvolveMatrix::create() {
24 return new FilterConvolveMatrix();
25 }
27 FilterConvolveMatrix::~FilterConvolveMatrix()
28 {}
30 int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) {
31 NRPixBlock *in = slot.get(_input);
32 if (!in) {
33 g_warning("Missing source image for feConvolveMatrix (in=%d)", _input);
34 return 1;
35 }
37 if (bias!=0) {
38 g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix is correct!");
39 // The SVG specification implies that feConvolveMatrix is defined for premultiplied colors (which makes sense).
40 // It also says that bias should simply be added to the result for each color (without taking the alpha into account)
41 // However, it also says that one purpose of bias is "to have .5 gray value be the zero response of the filter".
42 // It seems sensible to indeed support the latter behaviour instead of the former, but this does appear to go against the standard.
43 // Note that Batik simply does not support bias!=0
44 }
46 NRPixBlock *out = new NRPixBlock;
48 nr_pixblock_setup_fast(out, in->mode,
49 in->area.x0, in->area.y0, in->area.x1, in->area.y1,
50 true);
52 unsigned char *in_data = NR_PIXBLOCK_PX(in);
53 unsigned char *out_data = NR_PIXBLOCK_PX(out);
55 unsigned int const width = in->area.x1 - in->area.x0;
56 unsigned int const height = in->area.y1 - in->area.y0;
58 if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) {
59 for (unsigned int y=targetY; y < height - (orderY - targetY); y++){
60 for (unsigned int x=targetX; x < width - (orderX - targetX); x++){
61 double result_R = 0;
62 double result_G = 0;
63 double result_B = 0;
64 double result_A = 0;
65 for (unsigned int i=0; i < orderY; i++){
66 for (int j=0; j < orderX; j++){
67 unsigned int index = 4*( x - targetX + j + width*(y - targetY + i) );
68 unsigned int kernel_index = orderX-j-1 + orderX*(orderY-i-1);
69 result_R += ( (double) in_data[index+0] * kernelMatrix[kernel_index] );
70 result_G += ( (double) in_data[index+1] * kernelMatrix[kernel_index] );
71 result_B += ( (double) in_data[index+2] * kernelMatrix[kernel_index] );
72 result_A += ( (double) in_data[index+3] * kernelMatrix[kernel_index] );
73 }
74 }
75 unsigned int out_index = 4*( x + width*y );
76 if( preserveAlpha ) {
77 out_data[out_index+3] = in_data[out_index+3];
78 } else {
79 out_data[out_index+3] = CLAMP_D_TO_U8(result_A / divisor + 255*bias);
80 }
81 out_data[out_index+0] = CLAMP_D_TO_U8_ALPHA(result_R / divisor + out_data[out_index+3]*bias, out_data[out_index+3]); // CLAMP includes rounding!
82 out_data[out_index+1] = CLAMP_D_TO_U8_ALPHA(result_G / divisor + out_data[out_index+3]*bias, out_data[out_index+3]);
83 out_data[out_index+2] = CLAMP_D_TO_U8_ALPHA(result_B / divisor + out_data[out_index+3]*bias, out_data[out_index+3]);
84 }
85 }
86 } else {
87 for (unsigned int y=targetY; y < height - (orderY - targetY); y++){
88 for (unsigned int x=targetX; x < width - (orderX - targetX); x++){
89 double result_R = 0;
90 double result_G = 0;
91 double result_B = 0;
92 double result_A = 0;
93 for (unsigned int i=0; i < orderY; i++){
94 for (unsigned int j=0; j < orderX; j++){
95 unsigned int index = 4*( x - targetX + j + width*(y - targetY + i) );
96 unsigned int kernel_index = orderX-j-1 + orderX*(orderY-i-1);
97 result_R += ( (double) in_data[index+0] * in_data[index+3] * kernelMatrix[kernel_index] );
98 result_G += ( (double) in_data[index+1] * in_data[index+3] * kernelMatrix[kernel_index] );
99 result_B += ( (double) in_data[index+2] * in_data[index+3] * kernelMatrix[kernel_index] );
100 result_A += ( (double) in_data[index+3] * kernelMatrix[kernel_index] );
101 }
102 }
103 unsigned int out_index = 4*( x + width*y );
104 if( preserveAlpha ) {
105 out_data[out_index+3] = in_data[out_index+3];
106 } else {
107 out_data[out_index+3] = CLAMP_D_TO_U8(result_A / divisor + 255*bias);
108 }
109 if (out_data[out_index+3]==0) {
110 out_data[out_index+0] = 0;
111 out_data[out_index+1] = 0;
112 out_data[out_index+2] = 0;
113 } else {
114 out_data[out_index+0] = CLAMP_D_TO_U8(result_R / (divisor*out_data[out_index+3]) + 255*bias); // CLAMP includes rounding!
115 out_data[out_index+1] = CLAMP_D_TO_U8(result_G / (divisor*out_data[out_index+3]) + 255*bias);
116 out_data[out_index+2] = CLAMP_D_TO_U8(result_B / (divisor*out_data[out_index+3]) + 255*bias);
117 }
118 }
119 }
120 }
122 out->empty = FALSE;
123 slot.set(_output, out);
124 return 0;
125 }
127 void FilterConvolveMatrix::set_targetX(int coord) {
128 targetX = coord;
129 }
131 void FilterConvolveMatrix::set_targetY(int coord) {
132 targetY = coord;
133 }
135 void FilterConvolveMatrix::set_orderX(int coord) {
136 orderX = coord;
137 }
139 void FilterConvolveMatrix::set_orderY(int coord) {
140 orderY = coord;
141 }
143 void FilterConvolveMatrix::set_divisor(double d) {
144 divisor = d;
145 }
147 void FilterConvolveMatrix::set_bias(double b) {
148 bias = b;
149 }
151 void FilterConvolveMatrix::set_kernelMatrix(std::vector<gdouble> &km) {
152 kernelMatrix = km;
153 }
155 void FilterConvolveMatrix::set_edgeMode(FilterConvolveMatrixEdgeMode mode){
156 edgeMode = mode;
157 }
159 void FilterConvolveMatrix::set_preserveAlpha(bool pa){
160 preserveAlpha = pa;
161 }
163 void FilterConvolveMatrix::area_enlarge(NRRectL &area, Geom::Matrix const &/*trans*/)
164 {
165 //Seems to me that since this filter's operation is resolution dependent,
166 // some spurious pixels may still appear at the borders when low zooming or rotating. Needs a better fix.
167 area.x0 -= targetX;
168 area.y0 -= targetY;
169 area.x1 += orderX - targetX;
170 area.y1 += orderY - targetY;
171 }
173 FilterTraits FilterConvolveMatrix::get_input_traits() {
174 return TRAIT_PARALLER;
175 }
177 } /* namespace Filters */
178 } /* namespace Inkscape */
180 /*
181 Local Variables:
182 mode:c++
183 c-file-style:"stroustrup"
184 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
185 indent-tabs-mode:nil
186 fill-column:99
187 End:
188 */
189 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :