Code

Fix to prevent convolvematrix.cpp from setting targetX/Y to zero if no value is given.
[inkscape.git] / src / display / nr-filter-convolve-matrix.cpp
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;
127 void FilterConvolveMatrix::set_targetX(int coord) {
128     targetX = coord;
131 void FilterConvolveMatrix::set_targetY(int coord) {
132     targetY = coord;
135 void FilterConvolveMatrix::set_orderX(int coord) {
136     orderX = coord;
139 void FilterConvolveMatrix::set_orderY(int coord) {
140     orderY = coord;
143 void FilterConvolveMatrix::set_divisor(double d) {
144     divisor = d;
147 void FilterConvolveMatrix::set_bias(double b) {
148     bias = b;
151 void FilterConvolveMatrix::set_kernelMatrix(std::vector<gdouble> &km) {
152     kernelMatrix = km;
155 void FilterConvolveMatrix::set_edgeMode(FilterConvolveMatrixEdgeMode mode){
156     edgeMode = mode;
159 void FilterConvolveMatrix::set_preserveAlpha(bool pa){
160     preserveAlpha = pa;
163 void FilterConvolveMatrix::area_enlarge(NRRectL &area, Geom::Matrix const &/*trans*/)
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;
173 FilterTraits FilterConvolveMatrix::get_input_traits() {
174     return TRAIT_PARALLER;
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 :