~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/display/nr-filter-convolve-matrix.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * feConvolveMatrix filter primitive renderer
 
3
 *
 
4
 * Authors:
 
5
 *   Felipe Corrêa da Silva Sanches <felipe.sanches@gmail.com>
 
6
 *   Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
 
7
 *
 
8
 * Copyright (C) 2007,2009 authors
 
9
 *
 
10
 * Released under GNU GPL, read the file 'COPYING' for more information
 
11
 */
 
12
 
 
13
#include "display/nr-filter-convolve-matrix.h"
 
14
#include "display/nr-filter-units.h"
 
15
#include "display/nr-filter-utils.h"
 
16
#include <vector>
 
17
 
 
18
namespace Inkscape {
 
19
namespace Filters {
 
20
 
 
21
FilterConvolveMatrix::FilterConvolveMatrix()
 
22
{}
 
23
 
 
24
FilterPrimitive * FilterConvolveMatrix::create() {
 
25
    return new FilterConvolveMatrix();
 
26
}
 
27
 
 
28
FilterConvolveMatrix::~FilterConvolveMatrix()
 
29
{}
 
30
 
 
31
template<bool PREMULTIPLIED, bool PRESERVE_ALPHA, bool X_LOWER, bool X_UPPER, bool Y_LOWER, bool Y_UPPER>
 
32
static inline void convolve2D_XY(unsigned int const x, unsigned int const y, unsigned char *const out_data, unsigned char const *const in_data, unsigned int const width, unsigned int const height, double const *const kernel, unsigned int const orderX, unsigned int const orderY, unsigned int const targetX, unsigned int const targetY, double const bias) {
 
33
    double result_R = 0;
 
34
    double result_G = 0;
 
35
    double result_B = 0;
 
36
    double result_A = 0;
 
37
 
 
38
    unsigned int iBegin = Y_LOWER ? targetY-y : 0; // Note that to prevent signed/unsigned problems this requires that y<=targetY (which is true)
 
39
    unsigned int iEnd   = Y_UPPER ? height+targetY-y : orderY; // And this requires that y<=height+targetY (which is trivially true), in addition it should be true that height+targetY-y<=orderY (or equivalently y>=height+targetY-orderY, which is true)
 
40
    unsigned int jBegin = X_LOWER ? targetX-x : 0;
 
41
    unsigned int jEnd   = X_UPPER ? width+targetX-x : orderX;
 
42
 
 
43
    for (unsigned int i=iBegin; i<iEnd; i++){
 
44
        for (int j=jBegin; j<jEnd; j++){
 
45
            unsigned int index = 4*( x - targetX + j + width*(y - targetY + i) );
 
46
            unsigned int kernel_index = orderX-j-1 + orderX*(orderY-i-1);
 
47
            double k = PREMULTIPLIED ? kernel[kernel_index] : in_data[index+3] * kernel[kernel_index];
 
48
            result_R += in_data[index+0] * k;
 
49
            result_G += in_data[index+1] * k;
 
50
            result_B += in_data[index+2] * k;
 
51
            result_A += in_data[index+3] * kernel[kernel_index];
 
52
        }
 
53
    }
 
54
 
 
55
    unsigned int const out_index = 4*( x + width*y );
 
56
    if (PRESERVE_ALPHA) {
 
57
        out_data[out_index+3] = in_data[out_index+3];
 
58
    } else if (PREMULTIPLIED) {
 
59
        out_data[out_index+3] = CLAMP_D_TO_U8(result_A + 255*bias);
 
60
    } else {
 
61
        out_data[out_index+3] = CLAMP_D_TO_U8(result_A + bias);
 
62
    }
 
63
    if (PREMULTIPLIED) {
 
64
        out_data[out_index+0] = CLAMP_D_TO_U8_ALPHA(result_R + out_data[out_index+3]*bias, out_data[out_index+3]); // CLAMP includes rounding!
 
65
        out_data[out_index+1] = CLAMP_D_TO_U8_ALPHA(result_G + out_data[out_index+3]*bias, out_data[out_index+3]);
 
66
        out_data[out_index+2] = CLAMP_D_TO_U8_ALPHA(result_B + out_data[out_index+3]*bias, out_data[out_index+3]);
 
67
    } else if (out_data[out_index+3]==0) {
 
68
        out_data[out_index+0] = 0; // TODO: Is there a more sensible value that can be used here?
 
69
        out_data[out_index+1] = 0;
 
70
        out_data[out_index+2] = 0;
 
71
    } else {
 
72
        out_data[out_index+0] = CLAMP_D_TO_U8(result_R / out_data[out_index+3] + bias); // CLAMP includes rounding!
 
73
        out_data[out_index+1] = CLAMP_D_TO_U8(result_G / out_data[out_index+3] + bias);
 
74
        out_data[out_index+2] = CLAMP_D_TO_U8(result_B / out_data[out_index+3] + bias);
 
75
    }
 
76
}
 
77
 
 
78
template<bool PREMULTIPLIED, bool PRESERVE_ALPHA, bool Y_LOWER, bool Y_UPPER>
 
79
static inline void convolve2D_Y(unsigned int const y, unsigned char *const out_data, unsigned char const *const in_data, unsigned int const width, unsigned int const height, double const *const kernel, unsigned int const orderX, unsigned int const orderY, unsigned int const targetX, unsigned int const targetY, double const bias) {
 
80
    // See convolve2D below for rationale.
 
81
 
 
82
    unsigned int const lowerEnd = std::min(targetX,width);
 
83
    unsigned int const upperBegin = width - std::min<unsigned int>(width,orderX - 1u - targetX);
 
84
    unsigned int const midXBegin = std::min(lowerEnd,upperBegin);
 
85
    unsigned int const midXEnd = std::max(lowerEnd,upperBegin);
 
86
 
 
87
    for (unsigned int x=0; x<midXBegin; x++) {
 
88
        convolve2D_XY<PREMULTIPLIED,PRESERVE_ALPHA,true,false,Y_LOWER,Y_UPPER>(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias);
 
89
    }
 
90
    if (lowerEnd==upperBegin) {
 
91
        // Do nothing, empty mid section
 
92
    } else if (lowerEnd<upperBegin) {
 
93
        // In the middle no bounds have to be adjusted
 
94
        for (unsigned int x=midXBegin; x<midXEnd; x++) {
 
95
            convolve2D_XY<PREMULTIPLIED,PRESERVE_ALPHA,false,false,Y_LOWER,Y_UPPER>(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias);
 
96
        }
 
97
    } else {
 
98
        // In the middle both bounds have to be adjusted
 
99
        for (unsigned int x=midXBegin; x<midXEnd; x++) {
 
100
            convolve2D_XY<PREMULTIPLIED,PRESERVE_ALPHA,true,true,Y_LOWER,Y_UPPER>(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias);
 
101
        }
 
102
    }
 
103
    for (unsigned int x=midXEnd; x<width; x++) {
 
104
        convolve2D_XY<PREMULTIPLIED,PRESERVE_ALPHA,false,true,Y_LOWER,Y_UPPER>(x, y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias);
 
105
    }
 
106
}
 
107
 
 
108
template<bool PREMULTIPLIED, bool PRESERVE_ALPHA>
 
109
static void convolve2D(unsigned char *const out_data, unsigned char const *const in_data, unsigned int const width, unsigned int const height, double const *const kernel, unsigned int const orderX, unsigned int const orderY, unsigned int const targetX, unsigned int const targetY, double const _bias) {
 
110
    double const bias = PREMULTIPLIED ? _bias : 255*_bias; // If we're using non-premultiplied values the bias is always multiplied by 255.
 
111
 
 
112
    // For the middle section it should hold that (for all i such that 0<=i<orderY):
 
113
    //   0 <= y - targetY + i < height
 
114
    //   targetY <= y && y < height + targetY - orderY + 1
 
115
    // In other words, for y<targetY i's lower bound needs to be adjusted and for y>=height+targetY-orderY+1 i's upper bound needs to be adjusted.
 
116
 
 
117
    unsigned int const lowerEnd = std::min(targetY,height);
 
118
    unsigned int const upperBegin = height - std::min<unsigned int>(height,orderY - 1u - targetY);
 
119
    unsigned int const midYBegin = std::min(lowerEnd,upperBegin);
 
120
    unsigned int const midYEnd = std::max(lowerEnd,upperBegin);
 
121
 
 
122
    for (unsigned int y=0; y<midYBegin; y++) {
 
123
        convolve2D_Y<PREMULTIPLIED,PRESERVE_ALPHA,true,false>(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias);
 
124
    }
 
125
    if (lowerEnd==upperBegin) {
 
126
        // Do nothing, empty mid section
 
127
    } else if (lowerEnd<upperBegin) {
 
128
        // In the middle no bounds have to be adjusted
 
129
        for (unsigned int y=midYBegin; y<midYEnd; y++) {
 
130
            convolve2D_Y<PREMULTIPLIED,PRESERVE_ALPHA,false,false>(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias);
 
131
        }
 
132
    } else {
 
133
        // In the middle both bounds have to be adjusted
 
134
        for (unsigned int y=midYBegin; y<midYEnd; y++) {
 
135
            convolve2D_Y<PREMULTIPLIED,PRESERVE_ALPHA,true,true>(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias);
 
136
        }
 
137
    }
 
138
    for (unsigned int y=midYEnd; y<height; y++) {
 
139
        convolve2D_Y<PREMULTIPLIED,PRESERVE_ALPHA,false,true>(y, out_data, in_data, width, height, kernel, orderX, orderY, targetX, targetY, bias);
 
140
    }
 
141
}
 
142
 
 
143
int FilterConvolveMatrix::render(FilterSlot &slot, FilterUnits const &/*units*/) {
 
144
    NRPixBlock *in = slot.get(_input);
 
145
    if (!in) {
 
146
        g_warning("Missing source image for feConvolveMatrix (in=%d)", _input);
 
147
        return 1;
 
148
    }
 
149
    if (orderX<=0 || orderY<=0) {
 
150
        g_warning("Empty kernel!");
 
151
        return 1;
 
152
    }
 
153
    if (targetX<0 || targetX>=orderX || targetY<0 || targetY>=orderY) {
 
154
        g_warning("Invalid target!");
 
155
        return 1;
 
156
    }
 
157
    if (kernelMatrix.size()!=(unsigned int)(orderX*orderY)) {
 
158
        g_warning("kernelMatrix does not have orderX*orderY elements!");
 
159
        return 1;
 
160
    }
 
161
 
 
162
    if (bias!=0) {
 
163
        g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix is correct!");
 
164
        // The SVG specification implies that feConvolveMatrix is defined for premultiplied colors (which makes sense).
 
165
        // It also says that bias should simply be added to the result for each color (without taking the alpha into account)
 
166
        // However, it also says that one purpose of bias is "to have .5 gray value be the zero response of the filter".
 
167
        // It seems sensible to indeed support the latter behaviour instead of the former, but this does appear to go against the standard.
 
168
        // Note that Batik simply does not support bias!=0
 
169
    }
 
170
    if (edgeMode!=CONVOLVEMATRIX_EDGEMODE_NONE) {
 
171
        g_warning("Inkscape only supports edgeMode=\"none\" (and a filter uses a different one)!");
 
172
        // Note that to properly support edgeMode the interaction with area_enlarge should be well understood (and probably something needs to change)
 
173
        // area_enlarge should NOT let Inkscape enlarge the area beyond the filter area, it should only enlarge the rendered area if a part of the object is rendered to make it overlapping (enough) with adjacent parts.
 
174
    }
 
175
 
 
176
    NRPixBlock *out = new NRPixBlock;
 
177
 
 
178
    nr_pixblock_setup_fast(out, in->mode,
 
179
                           in->area.x0, in->area.y0, in->area.x1, in->area.y1,
 
180
                           true);
 
181
 
 
182
    unsigned char *in_data = NR_PIXBLOCK_PX(in);
 
183
    unsigned char *out_data = NR_PIXBLOCK_PX(out);
 
184
 
 
185
    unsigned int const width = in->area.x1 - in->area.x0;
 
186
    unsigned int const height = in->area.y1 - in->area.y0;
 
187
 
 
188
    // Set up predivided kernel matrix
 
189
    std::vector<double> kernel(kernelMatrix);
 
190
    for(size_t i=0; i<kernel.size(); i++) {
 
191
        kernel[i] /= divisor; // The code that creates this object makes sure that divisor != 0
 
192
    }
 
193
 
 
194
    if (in->mode==NR_PIXBLOCK_MODE_R8G8B8A8P) {
 
195
        if (preserveAlpha) {
 
196
            convolve2D<true,true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias);
 
197
        } else {
 
198
            convolve2D<true,false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias);
 
199
        }
 
200
    } else {
 
201
        if (preserveAlpha) {
 
202
            convolve2D<false,true>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias);
 
203
        } else {
 
204
            convolve2D<false,false>(out_data, in_data, width, height, &kernel.front(), orderX, orderY, targetX, targetY, bias);
 
205
        }
 
206
    }
 
207
 
 
208
    out->empty = FALSE;
 
209
    slot.set(_output, out);
 
210
    return 0;
 
211
}
 
212
 
 
213
void FilterConvolveMatrix::set_targetX(int coord) {
 
214
    targetX = coord;
 
215
}
 
216
 
 
217
void FilterConvolveMatrix::set_targetY(int coord) {
 
218
    targetY = coord;
 
219
}
 
220
 
 
221
void FilterConvolveMatrix::set_orderX(int coord) {
 
222
    orderX = coord;
 
223
}
 
224
 
 
225
void FilterConvolveMatrix::set_orderY(int coord) {
 
226
    orderY = coord;
 
227
}
 
228
 
 
229
void FilterConvolveMatrix::set_divisor(double d) {
 
230
    divisor = d;
 
231
}
 
232
 
 
233
void FilterConvolveMatrix::set_bias(double b) {
 
234
    bias = b;
 
235
}
 
236
 
 
237
void FilterConvolveMatrix::set_kernelMatrix(std::vector<gdouble> &km) {
 
238
    kernelMatrix = km;
 
239
}
 
240
 
 
241
void FilterConvolveMatrix::set_edgeMode(FilterConvolveMatrixEdgeMode mode){
 
242
    edgeMode = mode;
 
243
}
 
244
 
 
245
void FilterConvolveMatrix::set_preserveAlpha(bool pa){
 
246
    preserveAlpha = pa;
 
247
}
 
248
 
 
249
void FilterConvolveMatrix::area_enlarge(NRRectL &area, Geom::Matrix const &/*trans*/)
 
250
{
 
251
    //Seems to me that since this filter's operation is resolution dependent,
 
252
    // some spurious pixels may still appear at the borders when low zooming or rotating. Needs a better fix.
 
253
    area.x0 -= targetX;
 
254
    area.y0 -= targetY;
 
255
    area.x1 += orderX - targetX - 1; // This makes sure the last row/column in the original image corresponds to the last row/column in the new image that can be convolved without adjusting the boundary conditions).
 
256
    area.y1 += orderY - targetY - 1;
 
257
}
 
258
 
 
259
FilterTraits FilterConvolveMatrix::get_input_traits() {
 
260
    return TRAIT_PARALLER;
 
261
}
 
262
 
 
263
} /* namespace Filters */
 
264
} /* namespace Inkscape */
 
265
 
 
266
/*
 
267
  Local Variables:
 
268
  mode:c++
 
269
  c-file-style:"stroustrup"
 
270
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
271
  indent-tabs-mode:nil
 
272
  fill-column:99
 
273
  End:
 
274
*/
 
275
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :