/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
#include "colorconv_config.h"
#include "cczoomrotationbase.h"

// Use default DLL entry point
#include "oscl_dll.h"
OSCL_DLL_ENTRY_POINT_DEFAULT()


/**************************************************************
ASSUMPTIONS:
1. zoom ratio must be in range (1, 3]
2. cannot do zoom in in one dimenstion and zoom out in another
3. caller should take care of the aspect ratio control
4. if there are margins, the caller should paint the background to the desired color
5. all the starting address MUST be multiple of 4, which is normally the case.

**************************************************************/


ColorConvertBase::ColorConvertBase(): _mRowPix(NULL), _mColPix(NULL), _mInitialized(false), _mState(0), _mYuvRange(false)
{
}


OSCL_EXPORT_REF ColorConvertBase::~ColorConvertBase()
{
    if (_mRowPix)
    {
        OSCL_ARRAY_DELETE(_mRowPix);
    }
    if (_mColPix)
    {
        OSCL_ARRAY_DELETE(_mColPix);
    }
}


int16 ColorConvertBase::GetCapability(void)
{
    int16 cap = 0;
#if CCROTATE
    cap |= CCSUPPORT_ROTATION;
#endif
#if CCSCALING
    cap |= CCSUPPORT_SCALING;
#endif
    return cap;
}


/**************************************************************
nRotation : Rotation direction is defined at run time
            0 -- No rotation
            1 -- 90 degree counter clockwise (default)
            2 -- 180 degree rotation (upside-down)
            3 -- 90 degree clockwise
            -1 -- flip left-right
            -2 -- flip and rotate 90 degree cnt clk
            -3 -- flip and rotate 180 degree
            -4 -- flip and rotate 270 degree
**************************************************************/
int32 ColorConvertBase::Init(int32 Src_width, int32 Src_height, int32 Src_pitch, int32 Dst_width, int32 Dst_height, int32 Dst_pitch, int32 nRotation)
{

    /* check the followings, must be multiple of 2 */
    if ((Src_pitch&1) || (Dst_pitch&1) || (Dst_height&1) || (Src_height&1) || (Src_width&1))
    {
        return 0;
    }

    _mInitialized = false;
    _mSrc_mheight = Src_height;
    _mRotation  =   nRotation;
    _mDisp.src_pitch = _mSrc_pitch  =   Src_pitch;
    _mDisp.dst_pitch = _mDst_pitch  =   Dst_pitch;
    _mDisp.src_width = _mSrc_width  =   Src_width;
    _mDisp.src_height = _mSrc_height =  Src_height;
    _mDisp.dst_width = _mDst_width  =   Dst_width;
    _mDisp.dst_height = _mDst_height =  Dst_height;

    /* Check support for rotation */
    if (_mRotation&0x1)
    {
        if (!(GetCapability()&CCSUPPORT_ROTATION))
        {
            return 0;
        }
    }

    _mIsFlip = false;
    if (_mRotation & 0x4)
    {
        _mIsFlip = true;
        _mRotation -= 4;
    }

    //For now, we only support the zoom ratio <=3
    if (_mRotation&0x1)
    {
        if (((Src_width*3) < Dst_height) || ((Src_height*3) < Dst_width))
        {
            return 0;
        }
    }
    else
    {
        if (((Src_width*3) < Dst_width) || ((Src_height*3) < Dst_height))
        {
            return 0;
        }
    }

    if (_mRowPix)
    {
        OSCL_ARRAY_DELETE(_mRowPix);
    }
    if (_mColPix)
    {
        OSCL_ARRAY_DELETE(_mColPix);
    }
    _mRowPix = NULL;
    _mColPix = NULL;

    if ((_mRotation&0x1) == 0)
    { /* no rotation */
        if ((_mDst_width != _mSrc_width) || (_mDst_height != _mSrc_height))
        { /* scaling */
            //calulate the Row
            int32 leavecode = 0;
            OSCL_TRY(leavecode,
                     _mRowPix = OSCL_ARRAY_NEW(uint8, _mSrc_width);
                     _mColPix = OSCL_ARRAY_NEW(uint8, _mSrc_height);
                    );
            OSCL_FIRST_CATCH_ANY(leavecode,
                                 if (_mRowPix)
        {
            OSCL_ARRAY_DELETE(_mRowPix);
            }
            if (_mColPix)
        {
            OSCL_ARRAY_DELETE(_mColPix);
            }
            _mRowPix = NULL;
            _mColPix = NULL;
            return 0;
                                );
            StretchLine(_mRowPix, _mSrc_width, _mDst_width);
            StretchLine(_mColPix, _mSrc_height, _mDst_height);
            _mIsZoom = true;
        }
        else
        {
            _mIsZoom = false;
        }
    }
    else
    { /* rotation,  */
        if ((_mDst_height != _mSrc_width) || (_mDst_width != _mSrc_height))
        { /* scaling */
            //calulate the Row
            int32 leavecode = 0;
            OSCL_TRY(leavecode,
                     _mRowPix = OSCL_ARRAY_NEW(uint8, _mSrc_height);
                     _mColPix = OSCL_ARRAY_NEW(uint8, _mSrc_width);
                    );
            OSCL_FIRST_CATCH_ANY(leavecode,
                                 if (_mRowPix)
        {
            OSCL_ARRAY_DELETE(_mRowPix);
            }
            if (_mColPix)
        {
            OSCL_ARRAY_DELETE(_mColPix);
            }
            _mRowPix = NULL;
            _mColPix = NULL;
            return 0;
                                );
            StretchLine(_mColPix, _mSrc_width, _mDst_height);
            StretchLine(_mRowPix, _mSrc_height, _mDst_width);
            _mIsZoom = true;
        }
        else
        {
            _mIsZoom = false;
        }
    }

    // Check support to scaling
    if (_mIsZoom)
    {
        if (!(GetCapability()&CCSUPPORT_SCALING))
        {
            return 0;
        }
    }

    _mInitialized = true;

    return 1;
}

void ColorConvertBase::StretchLine(uint8 *pLinePix, int32 iSrcLen, int32 iDstLen)
{
    // zoom ratio must be in range (1, 3]
    int32 e, i, j = 0, d, i2SrcLen, i2DstLen;
    bool bIsShrink = false;

    if (iSrcLen > iDstLen)
    {
        bIsShrink = true;
    }

    if (3*iSrcLen == iDstLen || 3*iDstLen == iSrcLen)
    {
        j = 3;
    }
    if (2*iSrcLen == iDstLen || 2*iDstLen == iSrcLen)
    {
        j = 2;
    }

    if (j)
    {
        if (bIsShrink)
        {
            oscl_memset(pLinePix, 0, iSrcLen);
            for (i = 0; i < iSrcLen; i += j)
            {
                pLinePix[i] = 1;
            }
        }
        else
        {
            oscl_memset(pLinePix, j, iSrcLen);
        }
        return;
    }

    //int32 xd1=0 int32 xd2=iDstLen, int32 xs1=0, int32 xs2=iSrcLen
    //int32 dx, dy2, e, d, dx2;
    //dx = xd2 - xd1;
    i2SrcLen = (iSrcLen - 1) << 1;//dy2 = (xs2 - xs1) << 1;
    e = i2SrcLen - (iDstLen - 1);//e = dy2 - dx;
    i2DstLen = (iDstLen - 1) << 1;//dx2 = dx << 1;
    i = 0;
    j = 0;
    for (d = 0; d < iDstLen /*dx*/; d++)
    {
        if (i <= iSrcLen - 1)
        {
            *(pLinePix + i) =  j;
        }
        if (e >= 0)
        {
            i++;
            e -= i2DstLen;
        }
        while (e >= 0)
        {
            if (i <= iSrcLen - 1)
            {
                *(pLinePix + i) =  j; /* zoom in case */
            }
            i++;
            e -= i2DstLen;
        }
        j++;
        e += i2SrcLen;
    }

    e = pLinePix[0];
    for (i = 1; i < iSrcLen; i++)
    {
        d = pLinePix[i];
        pLinePix[i] = (d - e); /* take difference between adjacent iRowPix[]. */
        e = d;
    }

    // add some check here to make sure that sum_{i=0...iSrcLen-1} pLinePix[i] = iDstLen
    e = 0;
    for (i = 0; i < iSrcLen; i++)
    {
        e += pLinePix[i];
    }

    if (e != iDstLen)
    {
        j = 0;
        while (e > iDstLen) /* sum greater than the destination */
        {
            while (pLinePix[j] == 0 && j < iSrcLen) /* find nonzero repetition pixel */
            {
                j++;
            }

            if (j < iSrcLen)
            {
                pLinePix[j]--;      /* remove pixel by one */
                e--;
                j++;                /* move to the next pixel */
            }
            else
            {
                j = 0;
            }
        }

        j = 0;
        while (e < iDstLen) /* sum less than the destination */
        {
            while (pLinePix[j] >= 3 && j < iSrcLen) /* find valid pixel */
            {
                j++;
            }

            if (j < iSrcLen)
            {
                pLinePix[j]++;  /* add pixel by one */
                e++;
                j++;            /* move to the next pixel */
            }
            else
            {
                j = 0;
            }
        }
    }
}
