/*
 * SSA_TransForm.java
 *
 * Created on 22 Март 2006 г., 17:10
 */

/**
 *
 * @author voit
 */
public class SSA_Transform
{
    private SSA_VideoClient videoClient;

    // Таблицы для преобразования YBR в RGB.
    private int tableCrToRed[] = new int[256];
    private int tableCrToGreen[] = new int[256];
    private int tableCbToGreen[] = new int[256];
    private int tableCbToBlue[] = new int[256];

    // Рабочий буфер.
    private float floatData[] = new float[768 * 576];

    // Данные фильтра.
    private float filterLow[] = new float[4];
    private float filterHi[] = new float[4];
    private float inBuf[] = new float[1280];
    private float outBuf[] = new float[1280];

    ////////////////////////////////////////////////////////////////////////////
    //
    SSA_Transform( SSA_VideoClient vc, int Color )
    {
        videoClient = vc;
		if(Color>0)
			initTables();

        filterLow[0] = (float)0.4829629131445341;
        filterLow[1] = (float)0.8365163037378077;
        filterLow[2] = (float)0.2241438680420134;
        filterLow[3] = (float)-0.1294095225512603;
        filterHi[0] = (float)-0.1294095225512603;
        filterHi[1] = (float)-0.2241438680420134;
        filterHi[2] = (float)0.8365163037378077;
        filterHi[3] = (float)-0.4829629131445341;
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    private void initTables()
    {
        ////////////////////////////////////////////////////////////////////////
        int i;
        ////////////////////////////////////////////////////////////////////////

        // Подготовка таблиц для наиболее быстрого преобразования BMP в YBR, и наоборот.

       for( i = 0; i < 256; i++ )
        {
            tableCrToRed[i] = (int) ((i - 128) * 1.402);
            tableCbToGreen[i] = (int) ((i - 128) * 0.34414);
            tableCrToGreen[i] = (int)((i - 128) * 0.71414);
            tableCbToBlue[i] = (int)((i - 128) * 1.772);
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    // Обратное вейвлет-преобразование.
    //
    protected void waveletTransformReverse( short srcData[], short destData[],
                                            int index, int widthMatrix, int heightMatrix,
                                            int transformDepth, int quantSizeArray[] )
    {
        ////////////////////////////////////////////////////////////////////////
        int i, j, k, transform;
        int curWd, curHt;
        int a1, a2, a3, a4, b1, b2, b3, b4;
        int base;
        int offset;
        int pntLH, pntHL, pntHH, pntLL;
        int stepSize;
        ////////////////////////////////////////////////////////////////////////

        curWd = widthMatrix >> transformDepth;
        curHt = heightMatrix >> transformDepth;

        // Первый проход.

        k = 1 << (transformDepth - 1);

        pntLL = index;
        pntHL = curWd * curHt;
        pntLH = index + (pntHL << 1);
        pntHH = pntHL + pntLH;
        pntHL += index;

        offset = quantSizeArray[transformDepth - 1] - 2;

        stepSize = widthMatrix * k;

        for( i = 0; i < curHt; i++ )
        {
            base = index + ((i * stepSize) << 1);

            for( j = 0; j < curWd; j++ )
            {
                b1 = srcData[pntLL++];
                b2 = srcData[pntHL++];
                b3 = srcData[pntLH++];
                b4 = srcData[pntHH++];

                a1 = b1 + b2 + b3 + b4;
                a2 = b1 + b2 - b3 - b4;
                a3 = b1 - b2 + b3 - b4;
                a4 = b1 - b2 - b3 + b4;

                if( a1 < 0 )
                    destData[base] = (short) (-(-a1 << offset));
                else
                    destData[base] = (short) (a1 << offset);

                if( a2 < 0 )
                    destData[base + k] = (short) (-(-a2 << offset));
                else
                    destData[base + k] = (short) (a2 << offset);

                if( a3 < 0 )
                    destData[base + stepSize] = (short) (-(-a3 << offset));
                else
                    destData[base + stepSize] = (short) (a3 << offset);

                if( a4 < 0 )
                    destData[base + stepSize + k] = (short) (-(-a4 << offset));
                else
                    destData[base + stepSize + k] = (short) (a4 << offset);

                base += k << 1;
            }
        }

        for( transform = 1; transform < transformDepth; transform++ )
        {
            curWd <<= 1;
            curHt <<= 1;

            k >>= 1;

            pntHL = curWd * curHt;
            pntLH = index + (pntHL << 1);
            pntHH = pntHL + pntLH;
            pntHL += index;

            offset = quantSizeArray[transformDepth - transform - 1];

            stepSize = widthMatrix * k;

            for( i = 0; i < curHt; i++ )
            {
                base = index + ((i * stepSize) << 1);

                for( j = 0; j < curWd; j++ )
                {
                    b1 = destData[base];
                    b2 = srcData[pntHL++];
                    b3 = srcData[pntLH++];
                    b4 = srcData[pntHH++];

                    // Расквантование коэффициентов.

                    if( b2 < 0 )
                        b2 = -(-b2 << offset);
                    else
                        b2 <<= offset;

                    if( b3 < 0 )
                        b3 = -(-b3 << offset);
                    else
                        b3 <<= offset;

                    if( b4 < 0 )
                        b4 = -(-b4 << offset);
                    else
                        b4 <<= offset;

                    a1 = b1 + b2 + b3 + b4;
                    a2 = b1 + b2 - b3 - b4;
                    a3 = b1 - b2 + b3 - b4;
                    a4 = b1 - b2 - b3 + b4;

                    // Деление полученных значений на 4.

                    if( a1 < 0 )
                        a1 = -(-a1 >>> 2);
                    else
                        a1 >>>= 2;

                    if( a2 < 0 )
                        a2 = -(-a2 >>> 2);
                    else            
                        a2 >>>= 2;

                    if( a3 < 0 )
                        a3 = -(-a3 >>> 2);
                    else            
                        a3 >>>= 2;

                    if( a4 < 0 )     
                        a4 = -(-a4 >>> 2);
                    else            
                        a4 >>>= 2;


                    destData[base] = (short) a1;
                    destData[base + k] = (short) a2;
                    destData[base + stepSize] = (short) a3;
                    destData[base + stepSize + k] = (short) a4;

                    base += k << 1;
                }
            }
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    // Преобразовние кадра YCbCr 4:1:1 в RGB.
    //
    protected void transformYBRtoRGB( short srcData[], int destData[], int wdImage,
                                      int htImage )
    {
        ////////////////////////////////////////////////////////////////////////
        int y, r, g, b;
        int i, j, cb, cr;
        int cbIndex, crIndex;
        int indexColor, indexSrc, indexDest1, indexDest2;
        int rKoef, gKoef, bKoef;
        ////////////////////////////////////////////////////////////////////////

        cbIndex = wdImage * htImage;
        crIndex = cbIndex + (wdImage * htImage >> 2);

        indexColor = 0;
        indexSrc = 0;
        indexDest2 = cbIndex - wdImage;

        for( i = 0; i < (htImage >> 1); i++ )
        {
            for( j = 0; j < (wdImage >> 1); j++ )
            {
                indexDest1 = indexDest2 - wdImage;

                cb = srcData[cbIndex + indexColor];
                cr = srcData[crIndex + indexColor];

                if( cb < 0 ) 
                    cb = 0;
                else 
                    if( cb > 255 ) 
                        cb = 255;

                if( cr < 0 ) 
                    cr = 0;
                else 
                    if( cr > 255 ) 
                        cr = 255;

                bKoef = tableCrToRed[cr];
                gKoef = tableCbToGreen[cb] + tableCrToGreen[cr];
                rKoef = tableCbToBlue[cb];

                y = srcData[indexSrc];

                r = y + rKoef;

                if( r < 0 ) 
                    r = 0;
                else 
                    if( r > 255 ) 
                        r = 255;

                g = y - gKoef;

                if( g < 0 ) 
                    g = 0;
                else 
                    if( g > 255 ) 
                        g = 255;

                b = y + bKoef;

                if( b < 0 ) 
                    b = 0;
                else 
                    if( b > 255 ) 
                        b = 255;

                destData[indexDest2] = b | (g << 8) | (r << 16) | 0xFF000000;

                y = srcData[indexSrc + 1];

                r = y + rKoef;

                if( r < 0 ) 
                    r = 0;
                else 
                    if( r > 255 ) 
                        r = 255;

                g = y - gKoef;

                if( g < 0 ) 
                    g = 0;
                else
                    if( g > 255 ) 
                        g = 255;

                b = y + bKoef;

                if( b < 0 ) 
                    b = 0;
                else 
                    if( b > 255 ) 
                        b = 255;

                destData[indexDest2 + 1] = b | (g << 8) | (r << 16) | 0xFF000000;

                y = srcData[indexSrc + wdImage];

                r = y + rKoef;

                if( r < 0 ) 
                    r = 0;
                else 
                    if( r > 255 ) 
                        r = 255;

                g = y - gKoef;

                if( g < 0 ) 
                    g = 0;
                else 
                    if( g > 255 ) 
                        g = 255;

                b = y + bKoef;

                if( b < 0 ) 
                    b = 0;
                else 
                    if( b > 255 ) 
                        b = 255;

                destData[indexDest1] = b | (g << 8) | (r << 16) | 0xFF000000;

                y = srcData[indexSrc + wdImage + 1];

                r = y + rKoef;

                if( r < 0 ) 
                    r = 0;
                else 
                    if( r > 255 ) 
                        r = 255;

                g = y - gKoef;

                if( g < 0 ) 
                    g = 0;
                else 
                    if( g > 255 ) 
                        g = 255;

                b = y + bKoef;

                if( b < 0 ) 
                    b = 0;
                else 
                    if( b > 255 ) 
                        b = 255;

                destData[indexDest1 + 1] = b | (g << 8) | (r << 16) | 0xFF000000;

                indexColor++;
                indexSrc += 2;
                indexDest1 += 2;
                indexDest2 += 2;
            }

            indexSrc += wdImage;
            indexDest2 -= (wdImage * 3);
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    private void reverseFilterLine( int w )
    {
        ////////////////////////////////////////////////////////////////////////
        int i, hi, low;
        ///////////////////////////////////////////////////////////////////////

        low = 0;
        hi = w >> 1;

        outBuf[0] = inBuf[hi - 1] * filterLow[2] + inBuf[w - 1] * filterHi[2] +
                    inBuf[0] * filterLow[0] + inBuf[hi] * filterHi[0];

        outBuf[1] = inBuf[hi - 1] * filterLow[3] + inBuf[w - 1] * filterHi[3] +
                    inBuf[0] * filterLow[1] + inBuf[hi] * filterHi[1];

        for( i = 2; i < w; i += 2, low++, hi++ )
        {
            outBuf[i] = (float) (inBuf[low] * 0.2241438680420134 +
                        inBuf[hi] * 0.8365163037378077 +
                        inBuf[low + 1] * 0.4829629131445341 -
                        inBuf[hi + 1] * 0.1294095225512603);

            outBuf[i + 1] =  (float) (-inBuf[low] * 0.1294095225512603 -
                             inBuf[hi] * 0.4829629131445341 +
                             inBuf[low + 1] * 0.8365163037378077 -
                             inBuf[hi + 1] * 0.2241438680420134);
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    private void reverseFilter( int width, int begin, int step )
    {
        ///////////////////////////////////////////////////////////////////////
        int i, hi, low;
        ///////////////////////////////////////////////////////////////////////

        low = 0;
        hi = width >> 1;

        floatData[begin] = inBuf[hi - 1] * filterLow[2] + inBuf[width - 1] * filterHi[2] +
                           inBuf[0] * filterLow[0] + inBuf[hi] * filterHi[0];

        begin += step;

        floatData[begin] = inBuf[hi - 1] * filterLow[3] + inBuf[width - 1] * filterHi[3] +
                           inBuf[0] * filterLow[1] + inBuf[hi] * filterHi[1];

        begin += step;

        for( i = 2; i < width; i += 2, low++, hi++ )
        {
            floatData[begin] = (float) (inBuf[low] * 0.2241438680420134 +
                               inBuf[hi] * 0.8365163037378077 +
                               inBuf[low + 1] * 0.4829629131445341 -
                               inBuf[hi + 1] * 0.1294095225512603);

            begin += step;

            floatData[begin] = (float) (-inBuf[low] * 0.1294095225512603 -
                               inBuf[hi] * 0.4829629131445341 +
                               inBuf[low + 1] * 0.8365163037378077 -
                               inBuf[hi + 1] * 0.2241438680420134);

            begin += step;
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //
    private void reverseFilterOut( int width, int srcBegin, int dstBegin, int step, short dstData[] )
    {
        ////////////////////////////////////////////////////////////////////////
        int i, hi, low;
        ////////////////////////////////////////////////////////////////////////

        low = srcBegin;
        hi = srcBegin + (width >> 1) * step;

        dstData[dstBegin] = (short) (floatData[hi - step] * filterLow[2] +
                            floatData[width * step + srcBegin - step] * filterHi[2] +
                            floatData[srcBegin] * filterLow[0] +
                            floatData[hi] * filterHi[0]);

        dstBegin += step;

        dstData[dstBegin] = (short) (floatData[hi - step] * filterLow[3] +
                            floatData[width * step + srcBegin - step] * filterHi[3] +
                            floatData[srcBegin] * filterLow[1] +
                            floatData[hi] * filterHi[1]);

        dstBegin += step;

        for( i = 2; i < width; i += 2, low += step, hi += step )
        {
            dstData[dstBegin] = (short) (floatData[low] * 0.2241438680420134 +
                                floatData[hi] * 0.8365163037378077 +
                                floatData[low + step] * 0.4829629131445341 -
                                floatData[hi + step] * 0.1294095225512603);

            dstBegin += step;

            dstData[dstBegin] = (short) (-floatData[low] * 0.1294095225512603 -
                                floatData[hi] * 0.4829629131445341 +
                                floatData[low + step] * 0.8365163037378077 -
                                floatData[hi + step] * 0.2241438680420134);

            dstBegin += step;
        }
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    // Обратное вейвлет-преобразование.
    //
    protected void waveletTransformReverseFloat( short srcData[], short destData[],
                                                 int index, int widthMatrix,
                                                 int heightMatrix, int transformDepth,
                                                 int quantSizeArray[] )
    {
        ////////////////////////////////////////////////////////////////////////
        int i, j, transform, curWdImage, curHtImage, quant;
        int curWdHalf, curHtHalf, srcIndex, dstIndex, srcIndex2;
        short val;
        ////////////////////////////////////////////////////////////////////////

        // Преобразование первого уровня.

        curWdImage = widthMatrix >> (transformDepth - 1);
        curHtImage = heightMatrix >> (transformDepth - 1);
        curWdHalf = curWdImage >> 1;
        curHtHalf = curHtImage >> 1;

        quant = quantSizeArray[transformDepth - 1];

        for( i = 0; i < curHtImage; i++ )
        {
            srcIndex = i * widthMatrix + index;

            for( j = 0; j < curWdImage; j++, srcIndex++ )
            {
                if( (i < curHtHalf) && (j < curWdHalf) )
                    inBuf[j] = (float) srcData[srcIndex];
                else
                    inBuf[j] = (float) (srcData[srcIndex] << quant);
            }

            reverseFilter( curWdImage, i * widthMatrix, 1 );
        }

        for( j = 0; j < curWdImage; j++ )
        {
            srcIndex = j;

            for( i = 0; i < curHtImage; i++, srcIndex += widthMatrix )
                inBuf[i] = floatData[srcIndex];

            reverseFilter( curHtImage, j, widthMatrix );
        }

        // Преобразование следующих уровней.

        for( transform = transformDepth - 1; transform > 0; transform-- )
        {
            // Подсчёт размеров изображения на текущем уровне разложения.

            curWdImage = widthMatrix >> (transform - 1);
            curHtImage = heightMatrix >> (transform - 1);
            curWdHalf = curWdImage >> 1;
            curHtHalf = curHtImage >> 1;

            quant = quantSizeArray[transform - 1];

            for( i = 0; i < curHtImage; i++ )
            {
                srcIndex = i * widthMatrix;
                srcIndex2 = srcIndex + index;

                for( j = 0; j < curWdImage; j++, srcIndex++, srcIndex2++ )
                    if( (i < curHtHalf) && (j < curWdHalf) )
                        inBuf[j] = floatData[srcIndex];
                    else
                        inBuf[j] = (float) (srcData[srcIndex2] << quant);

                reverseFilter(curWdImage, i * widthMatrix, 1);
            }

            if( transform == 1 )
                for( j = 0; j < curWdImage; j++ )
                    reverseFilterOut( curHtImage, j, j + index, widthMatrix, destData );
            else
                for( j = 0; j < curWdImage; j++ )
                {
                    srcIndex = j;

                    for( i = 0; i < curHtImage; i++, srcIndex += widthMatrix )
                        inBuf[i] = floatData[srcIndex];

                    reverseFilter(curHtImage, j, widthMatrix);
                }
       }
    }
    //
    ////////////////////////////////////////////////////////////////////////////
}
