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 double result_R, result_G, result_B, result_A;
56 int i, j, x, y;
57 int width = in->area.x1 - in->area.x0;
58 int height = in->area.y1 - in->area.y0;
60 unsigned int index;
61 unsigned int kernel_index;
63 if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) {
64 for (y=targetY; y < height - (orderY - targetY); y++){
65 for (x=targetX; x < width - (orderX - targetX); x++){
66 result_R = 0;
67 result_G = 0;
68 result_B = 0;
69 result_A = 0;
70 for (i=0; i < orderY; i++){
71 for (j=0; j < orderX; j++){
72 index = 4*( x - targetX + j + width*(y - targetY + i) );
73 kernel_index = orderX-j-1 + orderX*(orderY-i-1);
74 result_R += ( (double) in_data[index++] * kernelMatrix[kernel_index] );
75 result_G += ( (double) in_data[index++] * kernelMatrix[kernel_index] );
76 result_B += ( (double) in_data[index++] * kernelMatrix[kernel_index] );
77 result_A += ( (double) in_data[index] * kernelMatrix[kernel_index] );
78 }
79 }
80 unsigned int out_index = 4*( x + width*y );
81 if( preserveAlpha ) {
82 out_data[out_index+3] = in_data[out_index+3];
83 } else {
84 out_data[out_index+3] = CLAMP_D_TO_U8(result_A / divisor + 255*bias);
85 }
86 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!
87 out_data[out_index+1] = CLAMP_D_TO_U8_ALPHA(result_G / divisor + out_data[out_index+3]*bias, out_data[out_index+3]);
88 out_data[out_index+2] = CLAMP_D_TO_U8_ALPHA(result_B / divisor + out_data[out_index+3]*bias, out_data[out_index+3]);
89 }
90 }
91 } else {
92 for (y=targetY; y < height - (orderY - targetY); y++){
93 for (x=targetX; x < width - (orderX - targetX); x++){
94 result_R = 0;
95 result_G = 0;
96 result_B = 0;
97 result_A = 0;
98 for (i=0; i < orderY; i++){
99 for (j=0; j < orderX; j++){
100 index = 4*( x - targetX + j + width*(y - targetY + i) );
101 kernel_index = orderX-j-1 + orderX*(orderY-i-1);
102 result_R += ( (double) in_data[index+0] * in_data[index+3] * kernelMatrix[kernel_index] );
103 result_G += ( (double) in_data[index+1] * in_data[index+3] * kernelMatrix[kernel_index] );
104 result_B += ( (double) in_data[index+2] * in_data[index+3] * kernelMatrix[kernel_index] );
105 result_A += ( (double) in_data[index+3] * kernelMatrix[kernel_index] );
106 }
107 }
108 unsigned int out_index = 4*( x + width*y );
109 if( preserveAlpha ) {
110 out_data[out_index+3] = in_data[out_index+3];
111 } else {
112 out_data[out_index+3] = CLAMP_D_TO_U8(result_A / divisor + 255*bias);
113 }
114 if (out_data[out_index+3]==0) {
115 out_data[out_index+0] = 0;
116 out_data[out_index+1] = 0;
117 out_data[out_index+2] = 0;
118 } else {
119 out_data[out_index+0] = CLAMP_D_TO_U8(result_R / (divisor*out_data[out_index+3]) + 255*bias); // CLAMP includes rounding!
120 out_data[out_index+1] = CLAMP_D_TO_U8(result_G / (divisor*out_data[out_index+3]) + 255*bias);
121 out_data[out_index+2] = CLAMP_D_TO_U8(result_B / (divisor*out_data[out_index+3]) + 255*bias);
122 }
123 }
124 }
125 }
127 out->empty = FALSE;
128 slot.set(_output, out);
129 return 0;
130 }
132 void FilterConvolveMatrix::set_targetX(int coord) {
133 targetX = coord;
134 }
136 void FilterConvolveMatrix::set_targetY(int coord) {
137 targetY = coord;
138 }
140 void FilterConvolveMatrix::set_orderX(int coord) {
141 orderX = coord;
142 }
144 void FilterConvolveMatrix::set_orderY(int coord) {
145 orderY = coord;
146 }
148 void FilterConvolveMatrix::set_divisor(double d) {
149 divisor = d;
150 }
152 void FilterConvolveMatrix::set_bias(double b) {
153 bias = b;
154 }
156 void FilterConvolveMatrix::set_kernelMatrix(std::vector<gdouble> &km) {
157 kernelMatrix = km;
158 }
160 void FilterConvolveMatrix::set_edgeMode(FilterConvolveMatrixEdgeMode mode){
161 edgeMode = mode;
162 }
164 void FilterConvolveMatrix::set_preserveAlpha(bool pa){
165 preserveAlpha = pa;
166 }
168 void FilterConvolveMatrix::area_enlarge(NRRectL &area, Geom::Matrix const &/*trans*/)
169 {
170 //Seems to me that since this filter's operation is resolution dependent,
171 // some spurious pixels may still appear at the borders when low zooming or rotating. Needs a better fix.
172 area.x0 -= targetX;
173 area.y0 -= targetY;
174 area.x1 += orderX - targetX;
175 area.y1 += orderY - targetY;
176 }
178 FilterTraits FilterConvolveMatrix::get_input_traits() {
179 return TRAIT_PARALLER;
180 }
182 } /* namespace Filters */
183 } /* namespace Inkscape */
185 /*
186 Local Variables:
187 mode:c++
188 c-file-style:"stroustrup"
189 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
190 indent-tabs-mode:nil
191 fill-column:99
192 End:
193 */
194 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :