Evas filters: Implement blur functions
authorJean-Philippe Andre <jp.andre@samsung.com>
Mon, 9 Dec 2013 07:26:02 +0000 (16:26 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Fri, 7 Feb 2014 08:33:16 +0000 (17:33 +0900)
Currently supported:
- Box blur for Alpha and RGBA
- Gaussian blur for Alpha and RGBA

Motion blur is not implemented.

These effects are SLOW. Gaussian blur is based on a true gaussian
curve for small radii, or a sine-based approximation.
The true gaussian might need to be removed for consistency, since
it gives slightly different results from the sine one (less blur).

It is not possible to mix Alpha and RGBA surfaces in this filter.

src/lib/evas/filters/evas_filter_blur.c [new file with mode: 0644]

diff --git a/src/lib/evas/filters/evas_filter_blur.c b/src/lib/evas/filters/evas_filter_blur.c
new file mode 100644 (file)
index 0000000..1980774
--- /dev/null
@@ -0,0 +1,741 @@
+#include "evas_filter.h"
+#include "evas_filter_private.h"
+
+#include <math.h>
+#include <time.h>
+
+#define DEBUG_TIME 1
+
+
+#if DIV_USING_BITSHIFT
+static int
+_smallest_pow2_larger_than(int val)
+{
+   int n;
+
+   for (n = 0; n < 32; n++)
+     if (val <= (1 << n)) return n;
+
+   ERR("Value %d is too damn high!", val);
+   return 32;
+}
+
+/* Input:
+ *  const int pow2 = _smallest_pow2_larger_than(divider * 1024);
+ *  const int numerator = (1 << pow2) / divider;
+ * Result:
+ *  r = ((val * numerator) >> pow2);
+ */
+# define DEFINE_DIAMETER(rad) const int pow2 = _smallest_pow2_larger_than((radius * 2 + 1) << 10); const int numerator = (1 << pow2) / (radius * 2 + 1);
+# define DIVIDE_BY_DIAMETER(val) (((val) * numerator) >> pow2)
+#else
+# define DEFINE_DIAMETER(rad) const int diameter = radius * 2 + 1;
+# define DIVIDE_BY_DIAMETER(val) ((val) / diameter)
+#endif
+
+// Switch from Pascal Triangle based gaussian to Sine
+#define MAX_GAUSSIAN_RADIUS 5
+#if MAX_GAUSSIAN_RADIUS > 12
+# error Impossible value
+#endif
+
+#if DEBUG_TIME
+# define DEBUG_TIME_BEGIN() \
+   struct timespec ts1, ts2; \
+   clock_gettime(CLOCK_MONOTONIC, &ts1);
+# define DEBUG_TIME_END() \
+   clock_gettime(CLOCK_MONOTONIC, &ts2); \
+   long long int t = 1000000LL * (ts2.tv_sec - ts1.tv_sec) \
+   + (ts2.tv_nsec - ts1.tv_nsec) / 1000LL; \
+   INF("TIME SPENT: %lldus", t);
+#else
+# define DEBUG_TIME_BEGIN() do {} while(0)
+# define DEBUG_TIME_END() do {} while(0)
+#endif
+
+/* RGBA functions */
+
+static void
+_box_blur_step_rgba(DATA32 *src, DATA32 *dst, int radius, int len, int step)
+{
+   DEFINE_DIAMETER(radius);
+   int acc[4] = {0};
+   DATA8 *d, *rs, *ls;
+   int x, k;
+   int divider;
+
+   d = (DATA8 *) dst;
+   ls = (DATA8 *) src;
+   rs = (DATA8 *) src;
+
+   // Read-ahead
+   for (x = radius; x; x--)
+     {
+        for (k = 0; k < 4; k++)
+          acc[k] += rs[k];
+        rs += step;
+     }
+
+   // Left
+   for (x = 0; x < radius; x++)
+     {
+        for (k = 0; k < 4; k++)
+          acc[k] += rs[k];
+        rs += step;
+
+        divider = x + radius + 1;
+        d[ALPHA] = acc[ALPHA] / divider;
+        d[RED]   = acc[RED]   / divider;
+        d[GREEN] = acc[GREEN] / divider;
+        d[BLUE]  = acc[BLUE]  / divider;
+        d += step;
+     }
+
+   // Main part
+   for (x = len - (2 * radius); x > 0; x--)
+     {
+        for (k = 0; k < 4; k++)
+          acc[k] += rs[k];
+        rs += step;
+
+        d[ALPHA] = DIVIDE_BY_DIAMETER(acc[ALPHA]);
+        d[RED]   = DIVIDE_BY_DIAMETER(acc[RED]);
+        d[GREEN] = DIVIDE_BY_DIAMETER(acc[GREEN]);
+        d[BLUE]  = DIVIDE_BY_DIAMETER(acc[BLUE]);
+        d += step;
+
+        for (k = 0; k < 4; k++)
+          acc[k] -= ls[k];
+        ls += step;
+     }
+
+   // Right part
+   for (x = radius; x; x--)
+     {
+        divider = x + radius;
+        d[ALPHA] = acc[ALPHA] / divider;
+        d[RED]   = acc[RED]   / divider;
+        d[GREEN] = acc[GREEN] / divider;
+        d[BLUE]  = acc[BLUE]  / divider;
+        d += step;
+
+        for (k = 0; k < 4; k++)
+          acc[k] -= ls[k];
+        ls += step;
+     }
+}
+
+static void
+_box_blur_horiz_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
+{
+   int y;
+   int step = sizeof(DATA32);
+
+   DEBUG_TIME_BEGIN();
+
+   for (y = 0; y < h; y++)
+     {
+        _box_blur_step_rgba(src, dst, radius, w, step);
+        src += w;
+        dst += w;
+     }
+
+   DEBUG_TIME_END();
+}
+
+static void
+_box_blur_vert_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
+{
+   int x;
+   int step = w * sizeof(DATA32);
+
+   DEBUG_TIME_BEGIN();
+
+   for (x = 0; x < w; x++)
+     {
+        _box_blur_step_rgba(src, dst, radius, h, step);
+        src += 1;
+        dst += 1;
+     }
+
+   DEBUG_TIME_END();
+}
+
+static Eina_Bool
+_box_blur_horiz_apply_rgba(Evas_Filter_Command *cmd)
+{
+   RGBA_Image *in, *out;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
+
+   in = cmd->input->backing;
+   out = cmd->output->backing;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
+
+   _box_blur_horiz_rgba(in->image.data, out->image.data, cmd->blur.dx,
+                        in->cache_entry.w, in->cache_entry.h);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_box_blur_vert_apply_rgba(Evas_Filter_Command *cmd)
+{
+   RGBA_Image *in, *out;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
+
+   in = cmd->input->backing;
+   out = cmd->output->backing;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
+
+   _box_blur_vert_rgba(in->image.data, out->image.data, cmd->blur.dy,
+                       in->cache_entry.w, in->cache_entry.h);
+
+   return EINA_TRUE;
+}
+
+/* Alpha only functions */
+
+/* Box blur */
+
+static void
+_box_blur_step_alpha(DATA8 *src, DATA8 *dst, int radius, int len, int step)
+{
+   int k;
+   int acc = 0;
+   DATA8 *sr = src, *sl = src, *d = dst;
+   DEFINE_DIAMETER(radius);
+
+   for (k = radius; k; k--)
+     {
+        acc += *sr;
+        sr += step;
+     }
+
+   for (k = 0; k < radius; k++)
+     {
+        acc += *sr;
+        *d = acc / (k + radius + 1);
+        sr += step;
+        d += step;
+     }
+
+   for (k = len - (2 * radius); k; k--)
+     {
+        acc += *sr;
+        *d = DIVIDE_BY_DIAMETER(acc);
+        acc -= *sl;
+        sl += step;
+        sr += step;
+        d += step;
+     }
+
+   for (k = radius; k; k--)
+     {
+        *d = acc / (k + radius);
+        acc -= *sl;
+        d += step;
+        sl += step;
+     }
+}
+
+static void
+_box_blur_horiz_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
+{
+   int k;
+
+   DEBUG_TIME_BEGIN();
+
+   for (k = h; k; k--)
+     {
+        _box_blur_step_alpha(src, dst, radius, w, 1);
+        dst += w;
+        src += w;
+     }
+
+   DEBUG_TIME_END();
+}
+
+static void
+_box_blur_vert_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
+{
+   int k;
+
+   DEBUG_TIME_BEGIN();
+
+   for (k = w; k; k--)
+     {
+        _box_blur_step_alpha(src, dst, radius, h, w);
+        dst += 1;
+        src += 1;
+     }
+
+   DEBUG_TIME_END();
+}
+
+static Eina_Bool
+_box_blur_horiz_apply_alpha(Evas_Filter_Command *cmd)
+{
+   RGBA_Image *in, *out;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
+
+   in = cmd->input->backing;
+   out = cmd->output->backing;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(in->mask.data, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(out->mask.data, EINA_FALSE);
+
+   _box_blur_horiz_alpha(in->mask.data, out->mask.data, cmd->blur.dx,
+                         in->cache_entry.w, in->cache_entry.h);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_box_blur_vert_apply_alpha(Evas_Filter_Command *cmd)
+{
+   RGBA_Image *in, *out;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
+
+   in = cmd->input->backing;
+   out = cmd->output->backing;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(in->mask.data, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(out->mask.data, EINA_FALSE);
+
+   _box_blur_vert_alpha(in->mask.data, out->mask.data, cmd->blur.dy,
+                        in->cache_entry.w, in->cache_entry.h);
+
+   return EINA_TRUE;
+}
+
+/* Gaussian blur */
+
+static void
+_gaussian_blur_weights_get(int *weights, int *pow2_divider, int radius)
+{
+   int even[radius + 1];
+   int odd[radius + 1];
+   int k, j;
+
+   EINA_SAFETY_ON_FALSE_RETURN(radius >= 0 && radius <= 12);
+
+   /* Uses Pascal's Triangle to compute the integer gaussian weights:
+    *
+    * 0            1             / 1     [1]
+    *            1   1                   [1 1]
+    * 1        1   2   1         / 4     [1 2]
+    *        1   3   3   1               [1 3 3]
+    * 2    1   4   6   4   1     / 16    [1 4 6]
+    *    1 ..................1
+    *
+    * Limitation: max radius is 12 when using 32 bits integers:
+    *  pow2_divider = 24, leaving exactly 8 bits for the data
+    */
+
+   if (pow2_divider)
+     *pow2_divider = radius * 2;
+
+   memset(odd, 0, sizeof(odd));
+   memset(even, 0, sizeof(even));
+   odd[0] = 1;
+   even[0] = 1;
+   for (k = 1; k <= radius; k++)
+     {
+        for (j = 1; j <= k; j++)
+          odd[j] = even[j] + even[j - 1];
+        odd[k] = 2 * even[k - 1];
+
+        for (j = 1; j <= k; j++)
+          even[j] = odd[j] + odd[j - 1];
+     }
+
+   for (k = 0; k <= radius; k++)
+     weights[k] = odd[k];
+   for (k = 0; k <= radius; k++)
+     weights[k + radius] = weights[radius - k];
+}
+
+static void
+_sin_blur_weights_get(int *weights, int *pow2_divider, int radius)
+{
+   const int diameter = 2 * radius + 1;
+   double x, divider, sum = 0.0;
+   double dweights[diameter];
+   int k, nextpow2, isum = 0;
+   const int FAKE_PI = 3.0;
+
+   /* Base curve:
+    * f(x) = sin(x+pi/2)/2+1/2
+    */
+
+   for (k = 0; k < diameter; k++)
+     {
+        x = ((double) k / (double) (diameter - 1)) * FAKE_PI * 2.0 - FAKE_PI;
+        dweights[k] = ((sin(x + M_PI_2) + 1.0) / 2.0) * 1024.0;
+        sum += dweights[k];
+     }
+
+   // Now we need to normalize to have a 2^N divider.
+   nextpow2 = log2(2 * sum);
+   divider = (double) (1 << nextpow2);
+
+   for (k = 0; k < diameter; k++)
+     {
+        weights[k] = round(dweights[k] * divider / sum);
+        isum += weights[k];
+     }
+
+   // Final correction. The difference SHOULD be small...
+   weights[radius] += (int) divider - isum;
+
+   if (pow2_divider)
+     *pow2_divider = nextpow2;
+}
+
+static void
+_gaussian_blur_step_alpha(DATA8 *src, DATA8 *dst, int radius, int len, int step,
+                          int *weights, int pow2_divider)
+{
+   int j, k, acc, divider;
+   DATA8 *s = src;
+   const int diameter = 2 * radius + 1;
+
+   // left
+   for (k = 0; k < radius; k++, dst += step)
+     {
+        acc = 0;
+        divider = 0;
+        s = src;
+        for (j = 0; j <= k + radius; j++, s += step)
+          {
+             acc += (*s) * weights[j + radius - k];
+             divider += weights[j + radius - k];
+          }
+        *dst = acc / divider;
+     }
+
+   // middle
+   for (k = radius; k < (len - radius); k++, src += step, dst += step)
+     {
+        acc = 0;
+        s = src;
+        for (j = 0; j < diameter; j++, s += step)
+          acc += (*s) * weights[j];
+        *dst = acc >> pow2_divider;
+     }
+
+   // right
+   for (k = 0; k < radius; k++, dst += step, src += step)
+     {
+        acc = 0;
+        divider = 0;
+        s = src;
+        for (j = 0; j < 2 * radius - k; j++, s += step)
+          {
+             acc += (*s) * weights[j];
+             divider += weights[j];
+          }
+        *dst = acc / divider;
+     }
+}
+
+static void
+_gaussian_blur_step_rgba(DATA32 *src, DATA32 *dst, int radius, int len, int step,
+                         int *weights, int pow2_divider)
+{
+   const int diameter = 2 * radius + 1;
+   int j, k;
+
+   // left
+   for (k = 0; k < radius; k++, dst += step)
+     {
+        int acc[4] = {0};
+        int divider = 0;
+        DATA32 *s = src;
+        for (j = 0; j <= k + radius; j++, s += step)
+          {
+             const int weightidx = j + radius - k;
+             acc[ALPHA] += A_VAL(s) * weights[weightidx];
+             acc[RED]   += R_VAL(s) * weights[weightidx];
+             acc[GREEN] += G_VAL(s) * weights[weightidx];
+             acc[BLUE]  += B_VAL(s) * weights[weightidx];
+             divider += weights[weightidx];
+          }
+        A_VAL(dst) = acc[ALPHA] / divider;
+        R_VAL(dst) = acc[RED]   / divider;
+        G_VAL(dst) = acc[GREEN] / divider;
+        B_VAL(dst) = acc[BLUE]  / divider;
+     }
+
+   // middle
+   for (k = len - (2 * radius); k > 0; k--, src += step, dst += step)
+     {
+        int acc[4] = {0};
+        DATA32 *s = src;
+        for (j = 0; j < diameter; j++, s += step)
+          {
+             acc[ALPHA] += A_VAL(s) * weights[j];
+             acc[RED]   += R_VAL(s) * weights[j];
+             acc[GREEN] += G_VAL(s) * weights[j];
+             acc[BLUE]  += B_VAL(s) * weights[j];
+          }
+        A_VAL(dst) = acc[ALPHA] >> pow2_divider;
+        R_VAL(dst) = acc[RED]   >> pow2_divider;
+        G_VAL(dst) = acc[GREEN] >> pow2_divider;
+        B_VAL(dst) = acc[BLUE]  >> pow2_divider;
+     }
+
+   // right
+   for (k = 0; k < radius; k++, dst += step, src += step)
+     {
+        int acc[4] = {0};
+        int divider = 0;
+        DATA32 *s = src;
+        for (j = 0; j < 2 * radius - k; j++, s += step)
+          {
+             acc[ALPHA] += A_VAL(s) * weights[j];
+             acc[RED]   += R_VAL(s) * weights[j];
+             acc[GREEN] += G_VAL(s) * weights[j];
+             acc[BLUE]  += B_VAL(s) * weights[j];
+             divider += weights[j];
+          }
+        A_VAL(dst) = acc[ALPHA] / divider;
+        R_VAL(dst) = acc[RED]   / divider;
+        G_VAL(dst) = acc[GREEN] / divider;
+        B_VAL(dst) = acc[BLUE]  / divider;
+     }
+}
+
+static void
+_gaussian_blur_horiz_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
+{
+   int *weights;
+   int k, pow2_div;
+
+   weights = alloca((2 * radius + 1) * sizeof(int));
+
+   if (radius <= MAX_GAUSSIAN_RADIUS)
+     _gaussian_blur_weights_get(weights, &pow2_div, radius);
+   else
+     _sin_blur_weights_get(weights, &pow2_div, radius);
+
+   for (k = h; k; k--)
+     {
+        _gaussian_blur_step_alpha(src, dst, radius, w, 1, weights, pow2_div);
+        dst += w;
+        src += w;
+     }
+}
+
+static void
+_gaussian_blur_vert_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
+{
+   int *weights;
+   int k, pow2_div;
+
+   weights = alloca((2 * radius + 1) * sizeof(int));
+
+   if (radius <= MAX_GAUSSIAN_RADIUS)
+     _gaussian_blur_weights_get(weights, &pow2_div, radius);
+   else
+     _sin_blur_weights_get(weights, &pow2_div, radius);
+
+   for (k = w; k; k--)
+     {
+        _gaussian_blur_step_alpha(src, dst, radius, h, w, weights, pow2_div);
+        dst += 1;
+        src += 1;
+     }
+}
+
+static void
+_gaussian_blur_horiz_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
+{
+   int *weights;
+   int k, pow2_div;
+
+   weights = alloca((2 * radius + 1) * sizeof(int));
+
+   if (radius <= MAX_GAUSSIAN_RADIUS)
+     _gaussian_blur_weights_get(weights, &pow2_div, radius);
+   else
+     _sin_blur_weights_get(weights, &pow2_div, radius);
+
+   for (k = h; k; k--)
+     {
+        _gaussian_blur_step_rgba(src, dst, radius, w, 1, weights, pow2_div);
+        dst += w;
+        src += w;
+     }
+}
+
+static void
+_gaussian_blur_vert_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
+{
+   int *weights;
+   int k, pow2_div;
+
+   weights = alloca((2 * radius + 1) * sizeof(int));
+
+   if (radius <= MAX_GAUSSIAN_RADIUS)
+     _gaussian_blur_weights_get(weights, &pow2_div, radius);
+   else
+     _sin_blur_weights_get(weights, &pow2_div, radius);
+
+   for (k = w; k; k--)
+     {
+        _gaussian_blur_step_rgba(src, dst, radius, h, w, weights, pow2_div);
+        dst += 1;
+        src += 1;
+     }
+}
+
+static Eina_Bool
+_gaussian_blur_horiz_apply_alpha(Evas_Filter_Command *cmd)
+{
+   RGBA_Image *in, *out;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
+
+   in = cmd->input->backing;
+   out = cmd->output->backing;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(in->mask.data, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(out->mask.data, EINA_FALSE);
+
+   _gaussian_blur_horiz_alpha(in->mask.data, out->mask.data, cmd->blur.dx,
+                              in->cache_entry.w, in->cache_entry.h);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_gaussian_blur_vert_apply_alpha(Evas_Filter_Command *cmd)
+{
+   RGBA_Image *in, *out;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
+
+   in = cmd->input->backing;
+   out = cmd->output->backing;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(in->mask.data, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(out->mask.data, EINA_FALSE);
+
+   _gaussian_blur_vert_alpha(in->mask.data, out->mask.data, cmd->blur.dy,
+                             in->cache_entry.w, in->cache_entry.h);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_gaussian_blur_horiz_apply_rgba(Evas_Filter_Command *cmd)
+{
+   RGBA_Image *in, *out;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
+
+   in = cmd->input->backing;
+   out = cmd->output->backing;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
+
+   _gaussian_blur_horiz_rgba(in->image.data, out->image.data, cmd->blur.dx,
+                             in->cache_entry.w, in->cache_entry.h);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_gaussian_blur_vert_apply_rgba(Evas_Filter_Command *cmd)
+{
+   RGBA_Image *in, *out;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
+
+   in = cmd->input->backing;
+   out = cmd->output->backing;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
+
+   _gaussian_blur_vert_rgba(in->image.data, out->image.data, cmd->blur.dy,
+                            in->cache_entry.w, in->cache_entry.h);
+
+   return EINA_TRUE;
+}
+
+/* Main entry point */
+
+Evas_Filter_Apply_Func
+evas_filter_blur_cpu_func_get(Evas_Filter_Command *cmd)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->mode == EVAS_FILTER_MODE_BLUR, NULL);
+
+   switch (cmd->blur.type)
+     {
+      case EVAS_FILTER_BLUR_BOX:
+        if (!cmd->input->alpha_only && !cmd->output->alpha_only)
+          {
+             if (cmd->blur.dx)
+               return _box_blur_horiz_apply_rgba;
+             else if (cmd->blur.dy)
+               return _box_blur_vert_apply_rgba;
+          }
+        else if (cmd->input->alpha_only && cmd->output->alpha_only)
+          {
+             if (cmd->blur.dx)
+               return _box_blur_horiz_apply_alpha;
+             else if (cmd->blur.dy)
+               return _box_blur_vert_apply_alpha;
+          }
+        CRIT("Unsupported operation: mixing RGBA and Alpha surfaces.");
+        return NULL;
+      case EVAS_FILTER_BLUR_GAUSSIAN:
+        if (!cmd->input->alpha_only && !cmd->output->alpha_only)
+          {
+             if (cmd->blur.dx)
+               return _gaussian_blur_horiz_apply_rgba;
+             else if (cmd->blur.dy)
+               return _gaussian_blur_vert_apply_rgba;
+          }
+        else if (cmd->input->alpha_only && cmd->output->alpha_only)
+          {
+             if (cmd->blur.dx)
+               return _gaussian_blur_horiz_apply_alpha;
+             else if (cmd->blur.dy)
+               return _gaussian_blur_vert_apply_alpha;
+          }
+        CRIT("Unsupported operation: mixing RGBA and Alpha surfaces.");
+        return NULL;
+      default:
+        CRIT("Not implemented yet!");
+        return NULL;
+     }
+}