/*
 * Copyright (C) 2007-2008 ARM Limited
 *
 * 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.
 *
 */
/**
 * 
 * File Name:  armVCM4P2_ACDCPredict.c
 * OpenMAX DL: v1.0.2
 * Revision:   9641
 * Date:       Thursday, February 7, 2008
 * 
 * 
 * 
 * 
 * Description:
 * Contains module for DC/AC coefficient prediction
 *
 */ 
 
#include "omxtypes.h"
#include "armOMX.h"

#include "armVC.h"
#include "armCOMM.h"

/**
 * Function: armVCM4P2_ACDCPredict
 *
 * Description:
 * Performs adaptive DC/AC coefficient prediction for an intra block. Prior
 * to the function call, prediction direction (predDir) should be selected
 * as specified in subclause 7.4.3.1 of ISO/IEC 14496-2.
 *
 * Remarks:
 *
 * Parameters:
 * [in] pSrcDst     pointer to the coefficient buffer which contains
 *                          the quantized coefficient residuals (PQF) of the
 *                          current block
 * [in] pPredBufRow pointer to the coefficient row buffer
 * [in] pPredBufCol pointer to the coefficient column buffer
 * [in] curQP       quantization parameter of the current block. curQP
 *                          may equal to predQP especially when the current
 *                          block and the predictor block are in the same
 *                          macroblock.
 * [in] predQP      quantization parameter of the predictor block
 * [in] predDir     indicates the prediction direction which takes one
 *                          of the following values:
 *                          OMX_VC_HORIZONTAL    predict horizontally
 *                          OMX_VC_VERTICAL      predict vertically
 * [in] ACPredFlag  a flag indicating if AC prediction should be
 *                          performed. It is equal to ac_pred_flag in the bit
 *                          stream syntax of MPEG-4
 * [in] videoComp   video component type (luminance, chrominance or
 *                          alpha) of the current block
 * [in] flag        This flag defines the if one wants to use this functions to
 *                  calculate PQF (set 1, prediction) or QF (set 0, reconstruction)
 * [out]    pPreACPredict   pointer to the predicted coefficients buffer.
 *                          Filled ONLY if it is not NULL
 * [out]    pSrcDst     pointer to the coefficient buffer which contains
 *                          the quantized coefficients (QF) of the current
 *                          block
 * [out]    pPredBufRow pointer to the updated coefficient row buffer
 * [out]    pPredBufCol pointer to the updated coefficient column buffer
 * [out]    pSumErr     pointer to the updated sum of the difference
 *                      between predicted and unpredicted coefficients
 *                      If this is NULL, do not update
 *
 * Return Value:
 * Standard OMXResult result. See enumeration for possible result codes.
 *
 */

OMXResult armVCM4P2_ACDCPredict(
     OMX_S16 * pSrcDst,
     OMX_S16 * pPreACPredict,
     OMX_S16 * pPredBufRow,
     OMX_S16 * pPredBufCol,
     OMX_INT curQP,
     OMX_INT predQP,
     OMX_INT predDir,
     OMX_INT ACPredFlag,
     OMXVCM4P2VideoComponent videoComp,
     OMX_U8 flag,
     OMX_INT *pSumErr
)
{
    OMX_INT dcScaler, i;
    OMX_S16 tempPred;

    /* Argument error checks */
    armRetArgErrIf(pSrcDst == NULL, OMX_Sts_BadArgErr);
    armRetArgErrIf(pPredBufRow == NULL, OMX_Sts_BadArgErr);
    armRetArgErrIf(pPredBufCol == NULL, OMX_Sts_BadArgErr);
    armRetArgErrIf(curQP <= 0, OMX_Sts_BadArgErr);
    armRetArgErrIf(predQP <= 0, OMX_Sts_BadArgErr);
    armRetArgErrIf((predDir != 1) && (predDir != 2), OMX_Sts_BadArgErr);
    armRetArgErrIf(!armIs4ByteAligned(pSrcDst), OMX_Sts_BadArgErr);
    armRetArgErrIf(!armIs4ByteAligned(pPredBufRow), OMX_Sts_BadArgErr);
    armRetArgErrIf(!armIs4ByteAligned(pPredBufCol), OMX_Sts_BadArgErr);

    
    /* Set DC scaler value to avoid some compilers giving a warning. */
    dcScaler=0;
    
    /* Calculate the DC scaler value */
    if (videoComp == OMX_VC_LUMINANCE)
    {
        if (curQP >= 1 && curQP <= 4)
        {
            dcScaler = 8;
        }
        else if (curQP >= 5 && curQP <= 8)
        {
            dcScaler = 2 * curQP;
        }
        else if (curQP >= 9 && curQP <= 24)
        {
            dcScaler = curQP + 8;
        }
        else
        {
            dcScaler = (2 * curQP) - 16;
        }
    }
    else if (videoComp == OMX_VC_CHROMINANCE)
    {
        if (curQP >= 1 && curQP <= 4)
        {
            dcScaler = 8;
        }
        else if (curQP >= 5 && curQP <= 24)
        {
            dcScaler = (curQP + 13)/2;
        }
        else
        {
            dcScaler = curQP - 6;
        }
    }

    if (pPreACPredict != NULL)
    {
        pPreACPredict[0] = predDir;
    }

    if (predDir == OMX_VC_VERTICAL)
    {
        /* F[0][0]//dc_scaler */
        tempPred = armIntDivAwayFromZero(pPredBufRow[0], dcScaler);
    }
    else
    {
        /* F[0][0]//dc_scaler */
        tempPred = armIntDivAwayFromZero(pPredBufCol[0], dcScaler);
    }

    /* Updating the DC value to the row and col buffer */
    *(pPredBufRow - 8) = *pPredBufCol;

    if (flag)
    {
        /* Cal and store F[0][0] into the col buffer */
        *pPredBufCol = pSrcDst[0] * dcScaler;

        /* PQF = QF - F[0][0]//dc_scaler */
        pSrcDst[0] -= tempPred;
    }
    else
    {
        /* QF = PQF + F[0][0]//dc_scaler */
        pSrcDst[0] += tempPred;
        
        /* Saturate */
        pSrcDst[0] = armClip (-2048, 2047, pSrcDst[0]);

        /* Cal and store F[0][0] into the col buffer */
        *pPredBufCol = pSrcDst[0] * dcScaler;
    }


    if (ACPredFlag == 1)
    {
        if (predDir == OMX_VC_VERTICAL)
        {
            for (i = 1; i < 8; i++)
            {
                tempPred = armIntDivAwayFromZero \
                              (pPredBufRow[i] * predQP, curQP);
                if (flag)
                {
                    /* Updating QF to the row buff */
                    pPredBufRow[i] = pSrcDst[i];
                    /*PQFX[v][0] = QFX[v][0] - (QFA[v][0] * QPA) // QPX */
                    pSrcDst[i] -= tempPred;
                    /* Sum of absolute values of AC prediction error, this can
                    be used as a reference to choose whether to use
                    AC prediction */
                    *pSumErr += armAbs(pSrcDst[i]);
                    /* pPreACPredict[1~7] store the error signal
                    after AC prediction */
                    pPreACPredict[i] = pSrcDst[i];
                }
                else
                {
                    /*QFX[v][0] = PQFX[v][0] + (QFA[v][0] * QPA) // QPX */
                    pSrcDst[i] += tempPred;
                    
                    /* Saturate */
                    pSrcDst[i] = armClip (-2048, 2047, pSrcDst[i]);
                    
                    /* Updating QF to the row buff */
                    pPredBufRow[i] = pSrcDst[i];
                }
            }
        }
        else
        {
            for (i = 8; i < 64; i += 8)
            {
                tempPred = armIntDivAwayFromZero \
                              (pPredBufCol[i>>3] * predQP, curQP);
                if (flag)
                {
                    /* Updating QF to col buff */
                    pPredBufCol[i>>3] = pSrcDst[i];
                    /*PQFX[0][u] = QFX[0][u] - (QFA[0][u] * QPA) // QPX */
                    pSrcDst[i] -= tempPred;
                    /* Sum of absolute values of AC prediction error, this can
                    be used as a reference to choose whether to use AC
                    prediction */
                    *pSumErr += armAbs(pSrcDst[i]);
                    /* pPreACPredict[1~7] store the error signal
                    after AC prediction */
                    pPreACPredict[i>>3] = pSrcDst[i];
                }
                else
                {
                    /*QFX[0][u] = PQFX[0][u] + (QFA[0][u] * QPA) // QPX */
                    pSrcDst[i] += tempPred;
                    
                    /* Saturate */
                    pSrcDst[i] = armClip (-2048, 2047, pSrcDst[i]);
                    
                    /* Updating QF to col buff */
                    pPredBufCol[i>>3] = pSrcDst[i];
                }
            }
        }
    }

    return OMX_Sts_NoErr;
}

/*End of File*/

