
#include "stdafx.h"
#include "basefwt.h"
#include "vec1d.h"



///////////////////////////////////constructors/destructors///////////////////////////////////////////////////////////////////
//constructor status 0-not initilized, 1,2-ok after init(), -1,-2,... errors
BaseFWT2D::BaseFWT2D(const wchar_t* fname) : m_status(0),
                tH(0), tG(0), H(0), G(0), H2m(0), G2m(0), H2m1(0), G2m1(0),
                J(0), TH(0), m_width(0), m_height(0),
                spec(0), tspec(0), spec2d(0), tspec2d(0)
{
        wcscpy(filter, fname);

        FILE* flt = _wfopen(fname, L"rt");
        if (flt) {
                if ((tH = loadfilter(flt)) == 0) {
                        m_status = -2;
                        return;
                }

                if ((tG = loadfilter(flt)) == 0) {
                        m_status = -3;
                        return;
                }

                if ((H = loadfilter(flt)) == 0) {
                        m_status = -4;
                        return;
                }

                if ((G = loadfilter(flt)) == 0) {
                        m_status = -5;
                        return;
                }

                fclose(flt);

                makeHGsynth();
        } else
                m_status = -1;    //filter file failed to open
}
BaseFWT2D::BaseFWT2D(const wchar_t* fname,
                     const float* tH, unsigned int thL, int thZ,
                     const float* tG, unsigned int tgL, int tgZ,
                     const float* H, unsigned int hL, int hZ,
                     const float* G, unsigned int gL, int gZ) : m_status(0),
                tH(0), tG(0), H(0), G(0), H2m(0), G2m(0), H2m1(0), G2m1(0),
                J(0), TH(0), m_width(0), m_height(0),
                spec(0), tspec(0), spec2d(0), tspec2d(0)
{
        wcscpy(filter, fname);

        this->tH = new vec1D(thL, -thZ, tH);
        this->tG = new vec1D(tgL, -tgZ, tG);
        this->H = new vec1D(hL, -hZ, H);
        this->G = new vec1D(gL, -gZ, G);

        makeHGsynth();
}
BaseFWT2D::~BaseFWT2D()
{
        if (tH) delete tH;
        if (tG) delete tG;
        if (H) delete H;
        if (G) delete G;
        if (H2m) delete H2m;
        if (G2m) delete G2m;
        if (H2m1) delete H2m1;
        if (G2m1) delete G2m1;
        close();                //close spec,tspec,spec2d,tspec2d buffers
}
///////////////////////////////////constructors/destructors///////////////////////////////////////////////////////////////////



//////////////////////////////////init/status functions//////////////////////////////////////////////////////////////////////
const wchar_t* BaseFWT2D::status(int& status) const
{
        status = m_status;

        switch (m_status) {

        default:
        case 0:
                return 0;

        case 1:
        case 2:
                return L"ready for transforms";

        case -1:
                return L"failed to open filter file";

        case -2:
                return L"failed to load tH filter";

        case -3:
                return L"failed to load tG filter";

        case -4:
                return L"failed to load H filter";

        case -5:
                return L"failed to load G filter";

        }
}

vec1D* BaseFWT2D::loadfilter(FILE* flt) const
{
        int L, Z;
        if (fwscanf(flt, L"%d %d", &L, &Z) != 2)
                return 0;

        vec1D* wf = new vec1D(L, -Z);
        for (int i = wf->first(); i <= wf->last(); i++) {
                float tmp;
                if (fwscanf(flt, L"%*d %f", &tmp) != 1)
                        return 0;
                else
                        (*wf)(i) = tmp;
        }
        return wf;
}

void BaseFWT2D::makeHGsynth()
{
        int size2m, offset2m;
        int size2m1, offset2m1;


        size2m = 0;
        size2m1 = 0;
        //arrange H2m,H2m1
        for (int m = H->first(); m <= H->last(); m++) {      //count how many odd even coeffs
                if (m % 2)
                        size2m1++;
                else
                        size2m++;
        }
        offset2m = (H->first() - (H->first() % 2)) / 2;
        offset2m1 = (H->first() + (H->first() % 2)) / 2;

        H2m = new vec1D(size2m, offset2m);
        H2m1 = new vec1D(size2m1, offset2m1);

        for (int m = H2m->first(); m <= H2m->last(); m++)
                (*H2m)(m) = (*H)(2 * m);
        for (int m = H2m1->first(); m <= H2m1->last(); m++)
                (*H2m1)(m) = (*H)(2 * m + 1);


        size2m = 0;
        size2m1 = 0;
        //arrange G2m,G2m1
        for (int m = G->first(); m <= G->last(); m++) {
                if (m % 2)
                        size2m1++;
                else
                        size2m++;
        }
        offset2m = (G->first() - (G->first() % 2)) / 2;
        offset2m1 = (G->first() + (G->first() % 2)) / 2;

        G2m = new vec1D(size2m, offset2m);
        G2m1 = new vec1D(size2m1, offset2m1);

        for (int m = G2m->first(); m <= G2m->last(); m++)
                (*G2m)(m) = (*G)(2 * m);
        for (int m = G2m1->first(); m <= G2m1->last(); m++)
                (*G2m1)(m) = (*G)(2 * m + 1);
}

void BaseFWT2D::tracefilters(const wchar_t* fname) const
{
        FILE* fp = _wfopen(fname, L"wt");
        if (fp) {
                fwprintf(fp, L"tH\n");
                for (int i = tH->first(); i <= tH->last(); i++)
                        fwprintf(fp, L" %2d  %g\n", i, (*tH)(i));
                fwprintf(fp, L"\ntG\n");
                for (int i = tG->first(); i <= tG->last(); i++)
                        fwprintf(fp, L" %2d  %g\n", i, (*tG)(i));
                fwprintf(fp, L"\nH\n");
                for (int i = H->first(); i <= H->last(); i++)
                        fwprintf(fp, L" %2d  %g\n", i, (*H)(i));
                fwprintf(fp, L"\nG\n");
                for (int i = G->first(); i <= G->last(); i++)
                        fwprintf(fp, L" %2d  %g\n", i, (*G)(i));

                fwprintf(fp, L"\n\nH2m\n");
                for (int i = H2m->first(); i <= H2m->last(); i++)
                        fwprintf(fp, L" %2d  %g\n", 2*i, (*H2m)(i));
                fwprintf(fp, L"\nH2m1\n");
                for (int i = H2m1->first(); i <= H2m1->last(); i++)
                        fwprintf(fp, L" %2d  %g\n", 2*i + 1, (*H2m1)(i));
                fwprintf(fp, L"\nG2m\n");
                for (int i = G2m->first(); i <= G2m->last(); i++)
                        fwprintf(fp, L" %2d  %g\n", 2*i, (*G2m)(i));
                fwprintf(fp, L"\nG2m1\n");
                for (int i = G2m1->first(); i <= G2m1->last(); i++)
                        fwprintf(fp, L" %2d  %g\n", 2*i + 1, (*G2m1)(i));

                fclose(fp);
        }
}

void BaseFWT2D::init(unsigned int width, unsigned int height)
{
        if (width != m_width || height != m_height) {
                close();
                m_width = width;
                m_height = height;

                spec = (char*)malloc(2 * width * height * sizeof(char));
                tspec = spec + width * height;

                spec2d = (char**)malloc(2 * height * sizeof(char*));     //setup rows
                for (unsigned int j = 0; j < 2*height; j++)
                        spec2d[j] = &spec[j*width];   //setup cols

                tspec2d = &spec2d[height];

                m_status = 1;
        }
}
void BaseFWT2D::init(char* data, char* tdata, unsigned int width, unsigned int height)
{
        close();
        m_width = width;
        m_height = height;

        spec = data;          //data - tdata  might be not continuous in memory
        tspec = tdata;

        spec2d = (char**)malloc(2 * height * sizeof(char *));     //setup rows
        for (unsigned int j = 0; j < height; j++)
                spec2d[j] = &spec[j*width];     //setup cols
        for (unsigned int j = 0; j < height; j++)
                spec2d[j+height] = &tspec[j*width];   //setup cols

        tspec2d = &spec2d[height];

        m_status = 2;
}
void BaseFWT2D::close(void)
{
        m_width = 0;
        m_height = 0;
        if (m_status == 1) {
                if (spec != 0) {
                        free(spec);
                        spec = 0;
                }
        }
        if (spec2d != 0) {
                free(spec2d);
                spec2d = 0;
        }
        m_status = 0;
}
//////////////////////////////////init/status functions//////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////transforms/////////////////////////////////////////////////////////////////////
int BaseFWT2D::trans(unsigned int scales, unsigned int th)
{
        if (m_status <= 0)
                return -1;

        J = scales;
        TH = th;
        unsigned int w = m_width;
        unsigned int h = m_height;

        for (unsigned int j = 0; j < J; j++) {
                transrows(tspec2d, spec2d, w, h);
                transcols(spec2d, tspec2d, w, h);
                w /= 2;
                h /= 2;
                TH /= 4;
        }

        return 0;
}

int BaseFWT2D::trans(const char* data, unsigned int scales, unsigned int th)
{
        if (m_status <= 0)
                return -1;

        mmxmemcpy(spec, data, m_width*m_height);    //copy data to spec
        return trans(scales, th);     //fwt transform
}

int BaseFWT2D::trans(const unsigned char* data, unsigned int scales, unsigned int th)
{
        if (m_status <= 0)
                return -1;

        sub128(spec, data, m_width*m_height);    //copy data to spec and correct DC
        return trans(scales, th);    //fwt transform
}
///////////////////////////////////////////////transforms/////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////synths//////////////////////////////////////////////////////////////////////////
int BaseFWT2D::synth()
{
        if (m_status <= 0)
                return -1;

        unsigned int w = m_width / (unsigned int)pow(2.0f, (float)J);
        unsigned int h = m_height / (unsigned int)pow(2.0f, (float)J);

        while (J) {
                synthcols(tspec2d, spec2d, w, h);
                synthrows(spec2d, tspec2d, w, h);
                w *= 2;
                h *= 2;
                J--;
        }

        return 0;
}

int BaseFWT2D::synth(char* data)
{
        int res;
        res = synth();
        if (res != 0)
                return res;
        mmxmemcpy(data, spec, m_width*m_height);      //copy restored spec to data
        return res;
}

int BaseFWT2D::synth(unsigned char* data)
{
        int res;
        res = synth();
        if (res != 0)
                return res;
        add128(data, spec, m_width*m_height);     //copy restored spec to data and correct DC
        return res;
}
//////////////////////////////////////////////synths//////////////////////////////////////////////////////////////////////////



//////////////////////////mmx routines////////////////////////////////////////////////////////////////////////////////////////
void BaseFWT2D::mmxmemcpy(char* dest, const char* sour, unsigned int size)
{
        //for(int i=0; i<m_width*m_height; i++)  //stub
        //dest[i] = sour[i];

        unsigned int modsize8 = size % 8;
        __m64* pdest = (__m64*)dest;
        const __m64* psour = (__m64*)sour;

        for (unsigned int i = 0; i < size / 8; i++)
                *pdest++ = *psour++;

        if (modsize8) {
                dest = (char*)pdest;
                sour = (char*)psour;
                for (unsigned int i = 0; i < modsize8; i++)
                        *dest++ = *sour++;
        }
        _mm_empty();
}

void BaseFWT2D::sub128(char* dest, const unsigned char* sour, unsigned int size)
{
        //for(int i=0; i<m_width*m_height; i++)
        // dest[i] = (char)((int)sour[i]-128);

        unsigned int modsize8 = size % 8;
        __m64 m128;
        m128.m64_u64 = 0x8080808080808080;
        __m64* pdest = (__m64*)dest;
        const __m64* psour = (__m64*)sour;

        for (unsigned int i = 0; i < size / 8; i++)
                *pdest++ = _mm_sub_pi8(*psour++ , m128);

        if (modsize8) {
                dest = (char*)pdest;
                sour = (unsigned char*)psour;
                for (unsigned int i = 0; i < modsize8; i++)
                        *dest++ = (char)((int) * sour++ - 128);
        }
        _mm_empty();
}
void BaseFWT2D::add128(unsigned char* dest, const char* sour, unsigned int size)
{
        //for(int i=0; i<m_width*m_height; i++)
        // data[i] = (unsigned char)((int)spec[i]+128);

        unsigned int modsize8 = size % 8;
        __m64 m128;
        m128.m64_u64 = 0x8080808080808080;
        __m64* pdest = (__m64*)dest;
        __m64* psour = (__m64*)sour;

        for (unsigned int i = 0; i < size / 8; i++)
                *pdest++ = _mm_add_pi8(*psour++ , m128);

        if (modsize8) {
                dest = (unsigned char*)pdest;
                sour = (char*)psour;
                for (unsigned int i = 0; i < modsize8; i++)
                        *dest++ = (unsigned char)((int) * sour++ + 128);
        }
        _mm_empty();
}

