/*
 * 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_BlockMatch_Half.c
 * OpenMAX DL: v1.0.2
 * Revision:   9641
 * Date:       Thursday, February 7, 2008
 * 
 * 
 * 
 * 
 * Description:
 * Contains modules for Block matching, a full search algorithm
 * is implemented
 *
 */

#include "omxtypes.h"
#include "armOMX.h"
#include "omxVC.h"

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

/**
 * Function: armVCM4P2_BlockMatch_Half
 *
 * Description:
 * Performs a 16x16 block match with half-pixel resolution.  Returns the estimated 
 * motion vector and associated minimum SAD.  This function estimates the half-pixel 
 * motion vector by interpolating the integer resolution motion vector referenced 
 * by the input parameter pSrcDstMV, i.e., the initial integer MV is generated 
 * externally.  The input parameters pSrcRefBuf and pSearchPointRefPos should be 
 * shifted by the winning MV of 16x16 integer search prior to calling BlockMatch_Half_16x16.  
 * The function BlockMatch_Integer_16x16 may be used for integer motion estimation.
 *
 * Remarks:
 *
 * Parameters:
 * [in]	pSrcRefBuf		pointer to the reference Y plane; points to the reference MB 
 *                    that corresponds to the location of the current macroblock in 
 *                    the	current plane.
 * [in]	refWidth		  width of the reference plane
 * [in]	pRefRect		  reference plane valid region rectangle
 * [in]	pSrcCurrBuf		pointer to the current macroblock extracted from original plane 
 *                    (linear array, 256 entries); must be aligned on an 8-byte boundary. 
 * [in]	pSearchPointRefPos	position of the starting point for half pixel search (specified 
 *                          in terms of integer pixel units) in the reference plane.
 * [in]	rndVal			  rounding control bit for half pixel motion estimation; 
 *                    0=rounding control disabled; 1=rounding control enabled
 * [in]	pSrcDstMV		pointer to the initial MV estimate; typically generated during a prior 
 *                  16X16 integer search and its unit is half pixel.
 * [in] BlockSize     MacroBlock Size i.e either 16x16 or 8x8.
 * [out]pSrcDstMV		pointer to estimated MV
 * [out]pDstSAD			pointer to minimum SAD
 *
 * Return Value:
 * OMX_Sts_NoErr ¨C no error
 * OMX_Sts_BadArgErr ¨C bad arguments
 *
 */

OMXResult armVCM4P2_BlockMatch_Half(
     const OMX_U8 *pSrcRefBuf,
     OMX_INT refWidth,
     const OMXRect *pRefRect,
     const OMX_U8 *pSrcCurrBuf,
     const OMXVCM4P2Coordinate *pSearchPointRefPos,
     OMX_INT rndVal,
     OMXVCMotionVector *pSrcDstMV,
     OMX_INT *pDstSAD,
     OMX_U8 BlockSize
)
{
    OMX_INT     outer, inner, count, index;
    OMX_S16     halfPelX = 0, halfPelY = 0, x, y;
    OMX_INT     candSAD, refSAD = 0;
    OMX_INT     minSAD, fromX, toX, fromY, toY;
    /* Offset to the reference at the begining of the bounding box */
    const OMX_U8      *pTempSrcRefBuf;
    OMX_U8 tempPel;
        
    /* Argument error checks */
    armRetArgErrIf(pSrcRefBuf == NULL, OMX_Sts_BadArgErr);
    armRetArgErrIf(pRefRect == NULL, OMX_Sts_BadArgErr);
    armRetArgErrIf(pSrcCurrBuf == NULL, OMX_Sts_BadArgErr);
    armRetArgErrIf(pSearchPointRefPos == NULL, OMX_Sts_BadArgErr);
    armRetArgErrIf(pSrcDstMV == NULL, OMX_Sts_BadArgErr);
    armRetArgErrIf(pDstSAD == NULL, OMX_Sts_BadArgErr);

    /* Positioning the pointer */
    pTempSrcRefBuf = pSrcRefBuf + (refWidth * (pSrcDstMV->dy/2)) + (pSrcDstMV->dx/2);

    /* Copy the candidate to the temporary linear array */
    for (outer = 0, count = 0,index = 0;
         outer < BlockSize;
         outer++, index += refWidth - BlockSize)
    {
        for (inner = 0; inner < BlockSize; inner++, count++, index++)
        {
            refSAD += armAbs (pTempSrcRefBuf[index] - pSrcCurrBuf[count]);
        }
    }

    /* Set the minSad as reference SAD */
    minSAD = refSAD;
    *pDstSAD = refSAD;

    /* Check for valid region */
    fromX = 1;
    toX   = 1;
    fromY = 1;
    toY   = 1;
    if ((pSearchPointRefPos->x - 1) < pRefRect->x)
    {
        fromX = 0;
    }

    if ((pSearchPointRefPos->x + BlockSize + 1) > (pRefRect->x + pRefRect->width))
    {
        toX   = 0;
    }

    if ((pSearchPointRefPos->y - 1) < pRefRect->y)
    {
        fromY = 0;
    }

    if ((pSearchPointRefPos->y + BlockSize + 1) > (pRefRect->y + pRefRect->height))
    {
        toY   = 0;
    }

    /* Looping on y- axis */
    for (y = -fromY; y <= toY; y++)
    {
        /* Looping on x- axis */
        for (x = -fromX; x <= toX; x++)
        {
            /* check for integer position */
            if ( x == 0 && y == 0)
            {
                continue;
            }
            /* Positioning the pointer */
            pTempSrcRefBuf = pSrcRefBuf + (refWidth * (pSrcDstMV->dy/2))
                             + (pSrcDstMV->dx/2);

            /* Interpolate the pixel and calculate the SAD*/
            for (outer = 0, count = 0, candSAD = 0,index = 0;
                 outer < BlockSize;
                 outer++, index += refWidth - BlockSize)
            {
                for (inner = 0; inner < BlockSize; inner++, count++,index++)
                {
                    tempPel = (
                                pTempSrcRefBuf[index]
                                + pTempSrcRefBuf[index + x] * armAbs(x)
                                + pTempSrcRefBuf[index + refWidth * y] * armAbs(y)
                                + pTempSrcRefBuf[index + refWidth * y + x]
                                  * armAbs(x*y)
                                + armAbs (x) + armAbs (y) - rndVal
                              ) / (2 * (armAbs (x) + armAbs (y)));
                    candSAD += armAbs (tempPel - pSrcCurrBuf[count]);
                }
            }

            /* Result calculations */
            if (armVCM4P2_CompareMV (x, y, candSAD, halfPelX, halfPelY, minSAD))
            {
                *pDstSAD = candSAD;
                minSAD   = candSAD;
                halfPelX = x;
                halfPelY = y;
            }

        } /* End of x- axis */
    } /* End of y-axis */

    pSrcDstMV->dx += halfPelX;
    pSrcDstMV->dy += halfPelY;

    return OMX_Sts_NoErr;

}

/* End of file */
