Code

Node tool: fix Tab and Shift+Tab
[inkscape.git] / src / display / nr-filter-gaussian.cpp
index 76b541ace61029b0ac3d855ccb1e99015b049dbc..5cfaab54dddb7d722a31a2da68955710b8f8c314 100644 (file)
@@ -8,17 +8,22 @@
  *   bulia byak
  *   Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
  *
- * Copyright (C) 2006 authors
+ * Copyright (C) 2006-2008 authors
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
+#include "config.h" // Needed for HAVE_OPENMP
+
 #include <algorithm>
 #include <cmath>
 #include <complex>
-#include <glib.h>
 #include <cstdlib>
+#include <glib.h>
 #include <limits>
+#if HAVE_OPENMP
+#include <omp.h>
+#endif //HAVE_OPENMP
 
 #include "2geom/isnan.h"
 
 #include "display/nr-filter-gaussian.h"
 #include "display/nr-filter-types.h"
 #include "display/nr-filter-units.h"
+#include "libnr/nr-blit.h"
 #include "libnr/nr-pixblock.h"
-#include "libnr/nr-matrix.h"
-#include "libnr/nr-matrix-fns.h"
+#include <2geom/matrix.h>
 #include "util/fixed_point.h"
-#include "prefs-utils.h"
+#include "preferences.h"
+
+#ifndef INK_UNUSED
+#define INK_UNUSED(x) ((void)(x))
+#endif
 
 // IIR filtering method based on:
 // L.J. van Vliet, I.T. Young, and P.W. Verbeek, Recursive Gaussian Derivative Filters,
@@ -50,7 +59,7 @@
 static size_t const N = 3;
 
 template<typename InIt, typename OutIt, typename Size>
-void copy_n(InIt beg_in, Size N, OutIt beg_out) {
+inline void copy_n(InIt beg_in, Size N, OutIt beg_out) {
     std::copy(beg_in, beg_in+N, beg_out);
 }
 
@@ -81,11 +90,12 @@ static inline Tt clip_round_cast(Ts const& v, Tt const minval=std::numeric_limit
     return round_cast<Tt>(v);
 }
 
-namespace NR {
+namespace Inkscape {
+namespace Filters {
 
 FilterGaussian::FilterGaussian()
 {
-    _deviation_x = _deviation_y = prefs_get_double_attribute("options.filtertest", "value", 1.0);
+    _deviation_x = _deviation_y = 0.0;
 }
 
 FilterPrimitive *FilterGaussian::create()
@@ -210,12 +220,6 @@ static void calcFilter(double const sigma, double b[N]) {
         // Compute scaled filter coefficients
         std::complex<double> const d1 = pow(d1_org, 1.0/q);
         double const d3 = pow(d3_org, 1.0/q);
-        double const absd1sqr = std::norm(d1);
-        double const re2d1 = 2*d1.real();
-        double const bscale = 1.0/(absd1sqr*d3);
-        b[2] = -bscale;
-        b[1] =  bscale*(d3+re2d1);
-        b[0] = -bscale*(absd1sqr+d3*re2d1);
         // Compute actual sigma^2
         double const ssqr = 2*(2*(d1/sqr(d1-1.)).real()+d3/sqr(d3-1.));
         if ( ssqr < sigmasqr ) {
@@ -225,6 +229,16 @@ static void calcFilter(double const sigma, double b[N]) {
         }
         s = sqrt(ssqr);
     } while(qend-qbeg>(sigma/(1<<30)));
+    // Compute filter coefficients
+    double const q = (qbeg+qend)/2;
+    std::complex<double> const d1 = pow(d1_org, 1.0/q);
+    double const d3 = pow(d3_org, 1.0/q);
+    double const absd1sqr = std::norm(d1); // d1*d2 = d1*conj(d1) = |d1|^2 = std::norm(d1)
+    double const re2d1 = 2*d1.real(); // d1+d2 = d1+conj(d1) = 2*real(d1)
+    double const bscale = 1.0/(absd1sqr*d3);
+    b[2] = -bscale;
+    b[1] =  bscale*(d3+re2d1);
+    b[0] = -bscale*(absd1sqr+d3*re2d1);
 }
 
 static void calcTriggsSdikaM(double const b[N], double M[N*N]) {
@@ -268,9 +282,19 @@ static void
 filter2D_IIR(PT *const dest, int const dstr1, int const dstr2,
              PT const *const src, int const sstr1, int const sstr2,
              int const n1, int const n2, IIRValue const b[N+1], double const M[N*N],
-             IIRValue *const tmpdata)
+             IIRValue *const tmpdata[], int const num_threads)
 {
+#if HAVE_OPENMP
+#pragma omp parallel for num_threads(num_threads)
+#else
+    INK_UNUSED(num_threads);
+#endif // HAVE_OPENMP
     for ( int c2 = 0 ; c2 < n2 ; c2++ ) {
+#if HAVE_OPENMP
+        unsigned int tid = omp_get_thread_num();
+#else
+        unsigned int tid = 0;
+#endif // HAVE_OPENMP
         // corresponding line in the source and output buffer
         PT const * srcimg = src  + c2*sstr2;
         PT       * dstimg = dest + c2*dstr2 + n1*dstr1;
@@ -288,7 +312,7 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2,
             for(unsigned int i=1; i<N+1; i++) {
                 for(unsigned int c=0; c<PC; c++) u[0][c] += u[i][c]*b[i];
             }
-            copy_n(u[0], PC, tmpdata+c1*PC);
+            copy_n(u[0], PC, tmpdata[tid]+c1*PC);
         }
         // Backward pass
         IIRValue v[N+1][PC];
@@ -303,7 +327,7 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2,
         int c1=n1-1;
         while(c1-->0) {
             for(unsigned int i=N; i>0; i--) copy_n(v[i-1], PC, v[i]);
-            copy_n(tmpdata+c1*PC, PC, v[0]);
+            copy_n(tmpdata[tid]+c1*PC, PC, v[0]);
             for(unsigned int c=0; c<PC; c++) v[0][c] *= b[0];
             for(unsigned int i=1; i<N+1; i++) {
                 for(unsigned int c=0; c<PC; c++) v[0][c] += v[i][c]*b[i];
@@ -321,16 +345,21 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2,
 
 // Filters over 1st dimension
 // Assumes kernel is symmetric
-// scr_len should be size of kernel - 1
+// Kernel should have scr_len+1 elements
 template<typename PT, unsigned int PC>
 static void
 filter2D_FIR(PT *const dst, int const dstr1, int const dstr2,
              PT const *const src, int const sstr1, int const sstr2,
-             int const n1, int const n2, FIRValue const *const kernel, int const scr_len)
+             int const n1, int const n2, FIRValue const *const kernel, int const scr_len, int const num_threads)
 {
     // Past pixels seen (to enable in-place operation)
     PT history[scr_len+1][PC];
 
+#if HAVE_OPENMP
+#pragma omp parallel for num_threads(num_threads) private(history)
+#else
+    INK_UNUSED(num_threads);
+#endif // HAVE_OPENMP
     for ( int c2 = 0 ; c2 < n2 ; c2++ ) {
 
         // corresponding line in the source buffer
@@ -348,7 +377,7 @@ filter2D_FIR(PT *const dst, int const dstr1, int const dstr2,
         for ( int c1 = 0 ; c1 < n1 ; c1++ ) {
 
             int const src_disp = src_line + c1 * sstr1;
-            int const dst_disp = dst_line + c1 * sstr1;
+            int const dst_disp = dst_line + c1 * dstr1;
 
             // update history
             for(int i=scr_len; i>0; i--) copy_n(history[i-1], PC, history[i]);
@@ -404,7 +433,7 @@ filter2D_FIR(PT *const dst, int const dstr1, int const dstr2,
                 // optimization: if there was no variation within this point's neighborhood,
                 // skip ahead while we keep seeing the same last_in byte:
                 // blurring flat color would not change it anyway
-                if (different_count <= 1) {
+                if (different_count <= 1) { // note that different_count is at least 1, because last_in is initialized to -1
                     int pos = c1 + 1;
                     int nb_src_disp = src_disp + (1+scr_len)*sstr1 + byte; // src_line + (pos+scr_len) * sstr1 + byte
                     int nb_dst_disp = dst_disp + (1)        *dstr1 + byte; // dst_line + (pos) * sstr1 + byte
@@ -412,7 +441,7 @@ filter2D_FIR(PT *const dst, int const dstr1, int const dstr2,
                         dst[nb_dst_disp] = last_in;
                         pos++;
                         nb_src_disp += sstr1;
-                        nb_dst_disp += sstr1;
+                        nb_dst_disp += dstr1;
                     }
                     skipbuf[byte] = pos;
                 }
@@ -510,42 +539,65 @@ upsample(PT *const dst, int const dstr1, int const dstr2, unsigned int const dn1
 
 int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
 {
-    /* in holds the input pixblock */
-    NRPixBlock *in = slot.get(_input);
-    if (!in) {
-        g_warning("Missing source image for feGaussianBlur (in=%d)", _input);
-        return 1;
-    }
+    // TODO: Meaningful return values? (If they're checked at all.)
 
-    Matrix trans = units.get_matrix_primitiveunits2pb();
+    /* in holds the input pixblock */
+    NRPixBlock *original_in = slot.get(_input);
 
     /* If to either direction, the standard deviation is zero or
      * input image is not defined,
      * a transparent black image should be returned. */
-    if (_deviation_x <= 0 || _deviation_y <= 0 || in == NULL) {
-        NRPixBlock *out = new NRPixBlock;
-        if (in == NULL) {
+    if (_deviation_x <= 0 || _deviation_y <= 0 || original_in == NULL) {
+        NRPixBlock *src = original_in;
+        if (src == NULL) {
+            g_warning("Missing source image for feGaussianBlur (in=%d)", _input);
             // A bit guessing here, but source graphic is likely to be of
             // right size
-            in = slot.get(NR_FILTER_SOURCEGRAPHIC);
+            src = slot.get(NR_FILTER_SOURCEGRAPHIC);
         }
-        nr_pixblock_setup_fast(out, in->mode, in->area.x0, in->area.y0,
-                               in->area.x1, in->area.y1, true);
-        if (out->data.px != NULL) {
+        NRPixBlock *out = new NRPixBlock;
+        nr_pixblock_setup_fast(out, src->mode, src->area.x0, src->area.y0,
+                               src->area.x1, src->area.y1, true);
+        if (out->size != NR_PIXBLOCK_SIZE_TINY && out->data.px != NULL) {
             out->empty = false;
             slot.set(_output, out);
         }
         return 0;
     }
 
+    // Gaussian blur is defined to operate on non-premultiplied color values.
+    //   So, convert the input first it uses non-premultiplied color values.
+    //   And please note that this should not be done AFTER resampling, as resampling a non-premultiplied image
+    //   does not simply yield a non-premultiplied version of the resampled premultiplied image!!!
+    NRPixBlock *in = original_in;
+    if (in->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
+        in = nr_pixblock_new_fast(NR_PIXBLOCK_MODE_R8G8B8A8P,
+                                  original_in->area.x0, original_in->area.y0,
+                                  original_in->area.x1, original_in->area.y1,
+                                  false);
+        if (!in) {
+            // ran out of memory
+            return 0;
+        }
+        nr_blit_pixblock_pixblock(in, original_in);
+    }
+
+    Geom::Matrix trans = units.get_matrix_primitiveunits2pb();
+
     // Some common constants
     int const width_org = in->area.x1-in->area.x0, height_org = in->area.y1-in->area.y0;
-    double const deviation_x_org = _deviation_x * NR::expansionX(trans);
-    double const deviation_y_org = _deviation_y * NR::expansionY(trans);
+    double const deviation_x_org = _deviation_x * trans.expansionX();
+    double const deviation_y_org = _deviation_y * trans.expansionY();
     int const PC = NR_PIXBLOCK_BPP(in);
+#if HAVE_OPENMP
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    int const NTHREADS = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256);
+#else
+    int const NTHREADS = 1;
+#endif // HAVE_OPENMP
 
     // Subsampling constants
-    int const quality = prefs_get_int_attribute("options.blurquality", "value", 0);
+    int const quality = slot.get_blurquality();
     int const x_step_l2 = _effect_subsample_step_log2(deviation_x_org, quality);
     int const y_step_l2 = _effect_subsample_step_log2(deviation_y_org, quality);
     int const x_step = 1<<x_step_l2;
@@ -572,19 +624,30 @@ int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
                                           in->area.x0/x_step+width, in->area.y0/y_step+height, true);
     if (out->size != NR_PIXBLOCK_SIZE_TINY && out->data.px == NULL) {
         // alas, we've accomplished a lot, but ran out of memory - so abort
+        if (in != original_in) nr_pixblock_free(in);
+        delete out;
         return 0;
     }
     // Temporary storage for IIR filter
     // NOTE: This can be eliminated, but it reduces the precision a bit
-    IIRValue * tmpdata = 0;
+    IIRValue * tmpdata[NTHREADS];
+    std::fill_n(tmpdata, NTHREADS, (IIRValue*)0);
     if ( use_IIR_x || use_IIR_y ) {
-        tmpdata = new IIRValue[std::max(width,height)*PC];
-        if (tmpdata == NULL) {
-            nr_pixblock_release(out);
-            delete out;
-            return 0;
+        for(int i=0; i<NTHREADS; i++) {
+            tmpdata[i] = new IIRValue[std::max(width,height)*PC];
+            if (tmpdata[i] == NULL) {
+                if (in != original_in) nr_pixblock_free(in);
+                nr_pixblock_release(out);
+                while(i-->0) {
+                    delete[] tmpdata[i];
+                }
+                delete out;
+                return 0;
+            }
         }
     }
+
+    // Resampling (if necessary), goes from in -> out (setting ssin to out if used)
     NRPixBlock *ssin = in;
     if ( resampling ) {
         ssin = out;
@@ -596,10 +659,10 @@ int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
         case NR_PIXBLOCK_MODE_R8G8B8:    ///< 8 bit RGB
             downsample<unsigned char,3>(NR_PIXBLOCK_PX(out), 3, out->rs, width, height, NR_PIXBLOCK_PX(in), 3, in->rs, width_org, height_org, x_step_l2, y_step_l2);
             break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
-            downsample<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, width, height, NR_PIXBLOCK_PX(in), 4, in->rs, width_org, height_org, x_step_l2, y_step_l2);
-            break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8P:  ///< Premultiplied 8 bit RGBA
+        //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
+        //    downsample<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, width, height, NR_PIXBLOCK_PX(in), 4, in->rs, width_org, height_org, x_step_l2, y_step_l2);
+        //    break;
+        case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA
             downsample<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, width, height, NR_PIXBLOCK_PX(in), 4, in->rs, width_org, height_org, x_step_l2, y_step_l2);
             break;
         default:
@@ -607,6 +670,7 @@ int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
         };
     }
 
+    // Horizontal filtering, goes from ssin -> out (ssin might be equal to out, but these algorithms can be used in-place)
     if (use_IIR_x) {
         // Filter variables
         IIRValue b[N+1];  // scaling coefficient + filter coefficients (can be 10.21 fixed point)
@@ -628,44 +692,47 @@ int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
         // Filter (x)
         switch(in->mode) {
         case NR_PIXBLOCK_MODE_A8:        ///< Grayscale
-            filter2D_IIR<unsigned char,1,false>(NR_PIXBLOCK_PX(out), 1, out->rs, NR_PIXBLOCK_PX(ssin), 1, ssin->rs, width, height, b, M, tmpdata);
+            filter2D_IIR<unsigned char,1,false>(NR_PIXBLOCK_PX(out), 1, out->rs, NR_PIXBLOCK_PX(ssin), 1, ssin->rs, width, height, b, M, tmpdata, NTHREADS);
             break;
         case NR_PIXBLOCK_MODE_R8G8B8:    ///< 8 bit RGB
-            filter2D_IIR<unsigned char,3,false>(NR_PIXBLOCK_PX(out), 3, out->rs, NR_PIXBLOCK_PX(ssin), 3, ssin->rs, width, height, b, M, tmpdata);
-            break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
-            filter2D_IIR<unsigned char,4,false>(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, b, M, tmpdata);
+            filter2D_IIR<unsigned char,3,false>(NR_PIXBLOCK_PX(out), 3, out->rs, NR_PIXBLOCK_PX(ssin), 3, ssin->rs, width, height, b, M, tmpdata, NTHREADS);
             break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8P:  ///< Premultiplied 8 bit RGBA
-            filter2D_IIR<unsigned char,4,true >(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, b, M, tmpdata);
+        //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
+        //    filter2D_IIR<unsigned char,4,false>(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, b, M, tmpdata, NTHREADS);
+        //    break;
+        case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA
+            filter2D_IIR<unsigned char,4,true >(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, b, M, tmpdata, NTHREADS);
             break;
         default:
             assert(false);
         };
-    } else { // !use_IIR_x
+    } else if ( scr_len_x > 0 ) { // !use_IIR_x
         // Filter kernel for x direction
-        FIRValue kernel[scr_len_x];
+        FIRValue kernel[scr_len_x+1];
         _make_kernel(kernel, deviation_x);
 
         // Filter (x)
         switch(in->mode) {
         case NR_PIXBLOCK_MODE_A8:        ///< Grayscale
-            filter2D_FIR<unsigned char,1>(NR_PIXBLOCK_PX(out), 1, out->rs, NR_PIXBLOCK_PX(ssin), 1, ssin->rs, width, height, kernel, scr_len_x);
+            filter2D_FIR<unsigned char,1>(NR_PIXBLOCK_PX(out), 1, out->rs, NR_PIXBLOCK_PX(ssin), 1, ssin->rs, width, height, kernel, scr_len_x, NTHREADS);
             break;
         case NR_PIXBLOCK_MODE_R8G8B8:    ///< 8 bit RGB
-            filter2D_FIR<unsigned char,3>(NR_PIXBLOCK_PX(out), 3, out->rs, NR_PIXBLOCK_PX(ssin), 3, ssin->rs, width, height, kernel, scr_len_x);
+            filter2D_FIR<unsigned char,3>(NR_PIXBLOCK_PX(out), 3, out->rs, NR_PIXBLOCK_PX(ssin), 3, ssin->rs, width, height, kernel, scr_len_x, NTHREADS);
             break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
-            filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, kernel, scr_len_x);
-            break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8P:  ///< Premultiplied 8 bit RGBA
-            filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, kernel, scr_len_x);
+        //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
+        //    filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, kernel, scr_len_x, NTHREADS);
+        //    break;
+        case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA
+            filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), 4, out->rs, NR_PIXBLOCK_PX(ssin), 4, ssin->rs, width, height, kernel, scr_len_x, NTHREADS);
             break;
         default:
             assert(false);
         };
+    } else if ( out != ssin ) { // out can be equal to ssin if resampling is used
+        nr_blit_pixblock_pixblock(out, ssin);
     }
 
+    // Vertical filtering, goes from out -> out
     if (use_IIR_y) {
         // Filter variables
         IIRValue b[N+1];  // scaling coefficient + filter coefficients (can be 10.21 fixed point)
@@ -687,46 +754,49 @@ int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
         // Filter (y)
         switch(in->mode) {
         case NR_PIXBLOCK_MODE_A8:        ///< Grayscale
-            filter2D_IIR<unsigned char,1,false>(NR_PIXBLOCK_PX(out), out->rs, 1, NR_PIXBLOCK_PX(out), out->rs, 1, height, width, b, M, tmpdata);
+            filter2D_IIR<unsigned char,1,false>(NR_PIXBLOCK_PX(out), out->rs, 1, NR_PIXBLOCK_PX(out), out->rs, 1, height, width, b, M, tmpdata, NTHREADS);
             break;
         case NR_PIXBLOCK_MODE_R8G8B8:    ///< 8 bit RGB
-            filter2D_IIR<unsigned char,3,false>(NR_PIXBLOCK_PX(out), out->rs, 3, NR_PIXBLOCK_PX(out), out->rs, 3, height, width, b, M, tmpdata);
-            break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
-            filter2D_IIR<unsigned char,4,false>(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, b, M, tmpdata);
+            filter2D_IIR<unsigned char,3,false>(NR_PIXBLOCK_PX(out), out->rs, 3, NR_PIXBLOCK_PX(out), out->rs, 3, height, width, b, M, tmpdata, NTHREADS);
             break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8P:  ///< Premultiplied 8 bit RGBA
-            filter2D_IIR<unsigned char,4,true >(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, b, M, tmpdata);
+        //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
+        //    filter2D_IIR<unsigned char,4,false>(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, b, M, tmpdata, NTHREADS);
+        //    break;
+        case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA
+            filter2D_IIR<unsigned char,4,true >(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, b, M, tmpdata, NTHREADS);
             break;
         default:
             assert(false);
         };
-    } else { // !use_IIR_y
+    } else if ( scr_len_y > 0 ) { // !use_IIR_y
         // Filter kernel for y direction
-        FIRValue kernel[scr_len_y];
+        FIRValue kernel[scr_len_y+1];
         _make_kernel(kernel, deviation_y);
 
         // Filter (y)
         switch(in->mode) {
         case NR_PIXBLOCK_MODE_A8:        ///< Grayscale
-            filter2D_FIR<unsigned char,1>(NR_PIXBLOCK_PX(out), out->rs, 1, NR_PIXBLOCK_PX(out), out->rs, 1, height, width, kernel, scr_len_y);
+            filter2D_FIR<unsigned char,1>(NR_PIXBLOCK_PX(out), out->rs, 1, NR_PIXBLOCK_PX(out), out->rs, 1, height, width, kernel, scr_len_y, NTHREADS);
             break;
         case NR_PIXBLOCK_MODE_R8G8B8:    ///< 8 bit RGB
-            filter2D_FIR<unsigned char,3>(NR_PIXBLOCK_PX(out), out->rs, 3, NR_PIXBLOCK_PX(out), out->rs, 3, height, width, kernel, scr_len_y);
+            filter2D_FIR<unsigned char,3>(NR_PIXBLOCK_PX(out), out->rs, 3, NR_PIXBLOCK_PX(out), out->rs, 3, height, width, kernel, scr_len_y, NTHREADS);
             break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
-            filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, kernel, scr_len_y);
-            break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8P:  ///< Premultiplied 8 bit RGBA
-            filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, kernel, scr_len_y);
+        //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
+        //    filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, kernel, scr_len_y, NTHREADS);
+        //    break;
+        case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA
+            filter2D_FIR<unsigned char,4>(NR_PIXBLOCK_PX(out), out->rs, 4, NR_PIXBLOCK_PX(out), out->rs, 4, height, width, kernel, scr_len_y, NTHREADS);
             break;
         default:
             assert(false);
         };
     }
 
-    delete[] tmpdata; // deleting a nullptr has no effect, so this is save
+    for(int i=0; i<NTHREADS; i++) {
+        delete[] tmpdata[i]; // deleting a nullptr has no effect, so this is safe
+    }
 
+    // Upsampling, stores (the upsampled) out using slot.set(_output, ...)
     if ( !resampling ) {
         // No upsampling needed
         out->empty = FALSE;
@@ -738,6 +808,7 @@ int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
                                                    in->area.x1, in->area.y1, true);
         if (finalout->size != NR_PIXBLOCK_SIZE_TINY && finalout->data.px == NULL) {
             // alas, we've accomplished a lot, but ran out of memory - so abort
+            if (in != original_in) nr_pixblock_free(in);
             nr_pixblock_release(out);
             delete out;
             return 0;
@@ -751,10 +822,10 @@ int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
         case NR_PIXBLOCK_MODE_R8G8B8:    ///< 8 bit RGB
             upsample<unsigned char,3>(NR_PIXBLOCK_PX(finalout), 3, finalout->rs, width_org, height_org, NR_PIXBLOCK_PX(out), 3, out->rs, width, height, x_step_l2, y_step_l2);
             break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
-            upsample<unsigned char,4>(NR_PIXBLOCK_PX(finalout), 4, finalout->rs, width_org, height_org, NR_PIXBLOCK_PX(out), 4, out->rs, width, height, x_step_l2, y_step_l2);
-            break;
-        case NR_PIXBLOCK_MODE_R8G8B8A8P:  ///< Premultiplied 8 bit RGBA
+        //case NR_PIXBLOCK_MODE_R8G8B8A8N: ///< Normal 8 bit RGBA
+        //    upsample<unsigned char,4>(NR_PIXBLOCK_PX(finalout), 4, finalout->rs, width_org, height_org, NR_PIXBLOCK_PX(out), 4, out->rs, width, height, x_step_l2, y_step_l2);
+        //    break;
+        case NR_PIXBLOCK_MODE_R8G8B8A8P: ///< Premultiplied 8 bit RGBA
             upsample<unsigned char,4>(NR_PIXBLOCK_PX(finalout), 4, finalout->rs, width_org, height_org, NR_PIXBLOCK_PX(out), 4, out->rs, width, height, x_step_l2, y_step_l2);
             break;
         default:
@@ -770,13 +841,16 @@ int FilterGaussian::render(FilterSlot &slot, FilterUnits const &units)
         slot.set(_output, finalout);
     }
 
+    // If we downsampled the input, clean up the downsampled data
+    if (in != original_in) nr_pixblock_free(in);
+
     return 0;
 }
 
-void FilterGaussian::area_enlarge(NRRectL &area, Matrix const &trans)
+void FilterGaussian::area_enlarge(NRRectL &area, Geom::Matrix const &trans)
 {
-    int area_x = _effect_area_scr(_deviation_x * NR::expansionX(trans));
-    int area_y = _effect_area_scr(_deviation_y * NR::expansionY(trans));
+    int area_x = _effect_area_scr(_deviation_x * trans.expansionX());
+    int area_y = _effect_area_scr(_deviation_y * trans.expansionY());
     // maximum is used because rotations can mix up these directions
     // TODO: calculate a more tight-fitting rendering area
     int area_max = std::max(area_x, area_y);
@@ -805,7 +879,8 @@ void FilterGaussian::set_deviation(double x, double y)
     }
 }
 
-} /* namespace NR */
+} /* namespace Filters */
+} /* namespace Inkscape */
 
 /*
   Local Variables:
@@ -816,4 +891,4 @@ void FilterGaussian::set_deviation(double x, double y)
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :