// Restrict to 7x7 kernel size for performance reasons
#define MX_MAX_SAMPLE_COUNT 49
// Size of all weights for all levels (including level 1)
#define MX_WEIGHT_ARRAY_SIZE 84

//
// Function to compute the sample size relative to a texture coordinate
//
vector2 mx_compute_sample_size_uv(vector2 uv, float filterSize, float filterOffset)
{
   vector derivUVx = Dx(vector(uv.x, uv.y, 0.0)) * 0.5;
   vector derivUVy = Dy(vector(uv.x, uv.y, 0.0)) * 0.5;
   float derivX = abs(derivUVx[0]) + abs(derivUVy[0]);
   float derivY = abs(derivUVx[1]) + abs(derivUVy[1]);
   float sampleSizeU = filterSize * derivX + filterOffset;
   if (sampleSizeU < 1.0E-05)
       sampleSizeU = 1.0E-05;
   float sampleSizeV = filterSize * derivY + filterOffset;
   if (sampleSizeV < 1.0E-05)
       sampleSizeV = 1.0E-05;
   return vector2(sampleSizeU, sampleSizeV);
}

// Kernel weights for box filter
void mx_get_box_weights(output float W[MX_MAX_SAMPLE_COUNT], int filterSize)
{
    int sampleCount = filterSize*filterSize;
    float value = 1.0 / float(sampleCount);
    for (int i=0; i<sampleCount; i++)
    {
        W[i] = value;
    }
}

// Kernel weights for Gaussian filter. Sigma is assumed to be 1.
void mx_get_gaussian_weights(output float W[MX_MAX_SAMPLE_COUNT], int filterSize)
{
    if (filterSize >= 7)
    {
        W[0] = 0.000036;  W[1] = 0.000363;  W[2] = 0.001446;  W[3] = 0.002291;  W[4] = 0.001446;  W[5] = 0.000363;  W[6] = 0.000036;
        W[7] = 0.000363;  W[8] = 0.003676;  W[9] = 0.014662;  W[10] = 0.023226; W[11] = 0.014662; W[12] = 0.003676; W[13] = 0.000363;
        W[14] = 0.001446; W[15] = 0.014662; W[16] = 0.058488; W[17] = 0.092651; W[18] = 0.058488; W[19] = 0.014662; W[20] = 0.001446;
        W[21] = 0.002291; W[22] = 0.023226; W[23] = 0.092651; W[24] = 0.146768; W[25] = 0.092651; W[26] = 0.023226; W[27] = 0.002291;
        W[28] = 0.001446; W[29] = 0.014662; W[30] = 0.058488; W[31] = 0.092651; W[32] = 0.058488; W[33] = 0.014662; W[34] = 0.001446;
        W[35] = 0.000363; W[36] = 0.003676; W[37] = 0.014662; W[38] = 0.023226; W[39] = 0.014662; W[40] = 0.003676; W[41] = 0.000363;
        W[42] = 0.000036; W[43] = 0.000363; W[44] = 0.001446; W[45] = 0.002291; W[46] = 0.001446; W[47] = 0.000363; W[48] = 0.000036;
    }
    else if (filterSize >= 5)
    {
        W[0] = 0.003765;  W[1] = 0.015019;  W[2] = 0.023792;  W[3] = 0.015019;  W[4] = 0.003765;
        W[5] = 0.015019;  W[6] = 0.059912;  W[7] = 0.094907;  W[8] = 0.059912;  W[9] = 0.015019;
        W[10] = 0.023792; W[11] = 0.094907; W[12] = 0.150342; W[13] = 0.094907; W[14] = 0.023792;
        W[15] = 0.015019; W[16] = 0.059912; W[17] = 0.094907; W[18] = 0.059912; W[19] = 0.015019;
        W[20] = 0.003765; W[21] = 0.015019; W[22] = 0.023792; W[23] = 0.015019; W[24] = 0.003765;
    }
    else if (filterSize >= 3)
    {
        W[0] = 0.0625; W[1] = 0.125; W[2] = 0.0625;
        W[3] = 0.125;  W[4] = 0.25;  W[5] = 0.125;
        W[6] = 0.0625; W[7] = 0.125; W[8] = 0.0625;
    }
    else
    {
        W[0] = 1.0;
    }
}

//
// Apply filter for float samples S, using weights W.
// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
//
float mx_convolution_float(float S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
{
    float result = 0.0;
    for (int i = 0;  i < sampleCount; i++)
    {
        result += S[i]*W[i+offset];
    }
    return result;
}

//
// Apply filter for vector2 samples S, using weights W.
// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
//
vector2 mx_convolution_vector2(vector2 S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
{
    vector2 result = vector2(0.0, 0.0);
    for (int i=0;  i<sampleCount; i++)
    {
        result += S[i]*W[i+offset];
    }
    return result;
}

//
// Apply filter for vector samples S, using weights W.
// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
//
vector mx_convolution_vector(vector S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
{
    vector result = vector(0.0, 0.0, 0.0);
    for (int i=0;  i<sampleCount; i++)
    {
        result += S[i]*W[i+offset];
    }
    return result;
}

//
// Apply filter for vector4 samples S, using weights W.
// sampleCount should be a square of a odd number { 1, 3, 5, 7 }
//
vector4 mx_convolution_vector4(vector4 S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
{
    vector4 result = vector4(0.0, 0.0, 0.0, 0.0);
    for (int i=0;  i<sampleCount; i++)
    {
        result += S[i]*W[i+offset];
    }
    return result;
}

//
// Apply filter for color samples S, using weights W.
// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
//
color mx_convolution_color(color S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
{
    color result = color(0.0);
    for (int i=0;  i<sampleCount; i++)
    {
        result += S[i]*W[i+offset];
    }
    return result;
}

//
// Apply filter for color4 samples S, using weights W.
// sampleCount should be a square of a odd number { 1, 3, 5, 7 }
//
color4 mx_convolution_color4(color4 S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
{
    color4 result = color4(color(0.0, 0.0, 0.0), 0.0);
    for (int i=0;  i<sampleCount; i++)
    {
        result += S[i]*W[i+offset];
    }
    return result;
}
