#include "imageblur.h"
#include <QtMath>
#include <QDebug>

//边缘算法
//i: 偏移
//x: 当前位置
//w: 范围(位置范围是0 -> w)
//返回: x周边第i个的位置
template <typename T>
T ImageBlurUtility::Edge(T i, T x, T w)
{
    //x周边第i个的位置
    T i_k = x + i;
    if      (i_k < 0)  i_k = -x;
    else if (i_k >= w) i_k = w - 1 - x;
    else               i_k = i;
    return i_k;
}
//归一化
void ImageBlurUtility::Normalization(std::vector<double> &kernels)
{
    double sum = std::accumulate(kernels.begin(), kernels.end(), 0.0);
    if(qFuzzyCompare(sum, 0)) return;
    if(qFuzzyCompare(sum, 1.0)) return;
    std::transform(kernels.begin(), kernels.end(), kernels.begin(),
                   [sum](double x){ return x/sum; });
}
//得到高斯核(把radius带入高斯公式求解)
std::vector<double> GaussBlur::GetKernels(int radius)
{
    static const double SQRT2PI = qSqrt(2.0 * 3.14159265358979323);

    double sigma = (double)radius / 3.0;
    double sigma2 = 2.0 * sigma * sigma;
    double sigmap = sigma * SQRT2PI;
    
    std::vector<double> kernels;
    for(int i = -radius; i <= radius; ++i)
    {
        kernels.push_back(qExp(-(double)(i * i) / sigma2) / sigmap);
    }
    return kernels;
}


void GaussBlur::Blur(QImage &image, int radius)
{
    std::vector<double> kernels = GetKernels(radius);
    ImageBlurUtility::Normalization(kernels);
    
    typedef ImageBlurUtility::pixel_t pixel; 
    typedef ImageBlurUtility::buff_t buff; 
    
    pixel *pData = (pixel*)image.bits();
    
    //栈的空间小, 所以这里buffer分配到堆
    const int size = image.width() * image.height();
    buff *pBuffer = new buff[size];
    
    for(int inx = 0, y = 0; y < image.height(); ++y)
    {
        for(int x = 0; x < image.width(); ++x, ++inx)
        {
            for(int n = 0, i = -radius; i <= radius; ++i, ++n)
            {
                long i_k = ImageBlurUtility::Edge(i, x, image.width());
                long inx_k = inx + i_k;
                pBuffer[inx].r += pData[inx_k].r() * kernels[n];
                pBuffer[inx].g += pData[inx_k].g() * kernels[n];
                pBuffer[inx].b += pData[inx_k].b() * kernels[n];
                pBuffer[inx].a += pData[inx_k].a() * kernels[n];
            }
        }
    }
    
    for(int inx = 0, x = 0; x < image.width(); ++x)
    {
        for(int y = 0; y < image.height(); ++y)
        {
            inx = y * image.width() + x;
            buff buffer;
            for(int n = 0, i = -radius; i <= radius; ++i, ++n)
            {
                int i_k = ImageBlurUtility::Edge(i, y, image.height());
                int inx_k = inx + i_k * image.width();
                buffer.r += pBuffer[inx_k].r * kernels[n];
                buffer.g += pBuffer[inx_k].g * kernels[n];
                buffer.b += pBuffer[inx_k].b * kernels[n];
                buffer.a += pBuffer[inx_k].a * kernels[n];
            }
            pData[inx].Set(buffer.Red(), buffer.Green(), buffer.Blue(), buffer.Alpha());
            //pData[inx].Set(Clamp<int>(r), Clamp<int>(g), Clamp<int>(b), Clamp<int>(a));
        }
    }
    delete[] pBuffer;
}