/* @file blur_gaussian_rgba_.c
 * Should define the functions:
 * - _gaussian_blur_horiz_rgba_step
 * - _gaussian_blur_vert_rgba_step
 */

#include "evas_filter_private.h"

#if !defined (FUNCTION_NAME) || !defined (STEP)
# error Must define FUNCTION_NAME and STEP
#endif

static inline void
FUNCTION_NAME(const DATA32* restrict srcdata, DATA32* restrict dstdata,
              const int radius, const int len,
              const int loops, const int loopstep,
              const int* restrict weights, const int pow2_divider)
{
   const int diameter = 2 * radius + 1;
   const int left = MIN(radius, len);
   const int right = MIN(radius, (len - radius));
   const DATA32* restrict src;
   DATA32* restrict dst;
   int i, j, k;

   for (i = loops; i; --i)
     {
        src = srcdata;
        dst = dstdata;

        // left
        for (k = 0; k < left; k++, dst += STEP)
          {
             int acc[4] = {0};
             int divider = 0;
             const DATA32* restrict 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];
               }
             if (!divider) goto error;
             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};
             const DATA32* restrict 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 < right; k++, dst += STEP, src += STEP)
          {
             int acc[4] = {0};
             int divider = 0;
             const DATA32* restrict 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];
               }
             if (!divider) goto error;
             A_VAL(dst) = acc[ALPHA] / divider;
             R_VAL(dst) = acc[RED]   / divider;
             G_VAL(dst) = acc[GREEN] / divider;
             B_VAL(dst) = acc[BLUE]  / divider;
          }

        dstdata += loopstep;
        srcdata += loopstep;
     }

   return;

error:
   CRI("Avoided division by 0.");
}

#undef FUNCTION_NAME
#undef STEP
