/*******************************************************************************
* Copyright 2016-2020 Intel Corporation.
*
* This software and the related documents are Intel copyrighted  materials,  and
* your use of  them is  governed by the  express license  under which  they were
* provided to you (License).  Unless the License provides otherwise, you may not
* use, modify, copy, publish, distribute,  disclose or transmit this software or
* the related documents without Intel's prior written permission.
*
* This software and the related documents  are provided as  is,  with no express
* or implied  warranties,  other  than those  that are  expressly stated  in the
* License.
*******************************************************************************/

#include "pifsobel_t.h"

/* /////////////////////////////////////////////////////////////////////////////
//  Name:               ippiFilterSobelHorizBorderGetBufferSize_T
//
//  Purpose:            Computes the size of the external buffer for Horizontal Sobel operator
//
//  Parameters:
//   roiSize            Size of destination ROI in pixels.
//   maskId             Predefined mask of IppiMaskSize type.
//   srcDataType        Data type of the source image.
//   dstDataType        Data type of the destination image.
//   numChannels        Number of channels in the images. Possible values is 1.
//   pBufferSize        Pointer to the size (in bytes) of the external work buffer.
//
//  Return Values:
//   ippStsNoErr        Indicates no error.
//   ippStsNullPtrErr   Indicates an error when pBufferSize is NULL.
//   ippStsSizeErr      Indicates an error when roiSize is negative, or equal to zero.
//   ippStsMaskSizeErr  Indicates an error condition if mask has a wrong value.
//   ippStsDataTypeErr  Indicates an error when srcDataType or dstDataType has an illegal value.
//   ippStsNumChannelsErr Indicates an error when numChannels has an illegal value.
*/
IPPFUN(IppStatus, ippiFilterSobelHorizBorderGetBufferSize_T, (IppiSize roiSize, IppiMaskSize maskId, IppDataType srcDataType,
                                                              IppDataType dstDataType, int numChannels, int *pBufferSize))
{
    IppStatus status = ippStsNoErr;

    if (pBufferSize == 0) return ippStsNullPtrErr;
    if (roiSize.width <= 0 || roiSize.height <= 0) return ippStsSizeErr;
    if (dstDataType != ipp16s && dstDataType != ipp32f) return ippStsDataTypeErr;

    Ipp32s numThreads = 1;
    IppiSize sliceSize, lastSliceSize;
    IppiPoint splitImage;

    int bufferSizeH   = 0;

    ippGetNumThreads_T(&numThreads);

    if (1 == numThreads)
    {
        status = ippiFilterSobelHorizBorderGetBufferSize(roiSize, maskId, srcDataType, dstDataType, numChannels, pBufferSize);
    }
    else
    {
        ownGetSobelSliceSize(roiSize, maskId, &sliceSize, &lastSliceSize, &splitImage);

        IppiSize maxSliceSize = {IPP_MAX(sliceSize.width, lastSliceSize.width), IPP_MAX(sliceSize.height, lastSliceSize.height)};

        status = ippiFilterSobelHorizBorderGetBufferSize(maxSliceSize, maskId, srcDataType, dstDataType, numChannels, &bufferSizeH);

        if (status >= 0)
        {
            *pBufferSize = bufferSizeH * ((int)numThreads);
        }
    }

    return status;
}

/* /////////////////////////////////////////////////////////////////////////////
//  Name:               ippiFilterSobelHorizBorder_8u16s_C1R_T_Fun
//
//  Purpose:            Kernels to be called in parallel_for of Threading Layer - performs linear filtering
//                      of particular slice of the image using one of predefined convolution kernels.
//
//  Parameters:
//   t                  thread index
//   arg                pointer to the Filter Sobel threading structure
//
//  Return Values:
//   ippStsNoErr            Indicates no error.
//   ippStsNullPtrErr       Indicates an error when pBufferSize is NULL.
//   ippStsSizeErr          Indicates an error when roiSize is negative, or equal to zero.
//   ippStsNotEvenStepErr   Indicated an error when one of the step values is not divisible by 4
//                          for floating-point images, or by 2 for short-integer images.
//   ippStsBorderErr        Indicates an error when borderType has illegal value.
*/
static IppStatus ippiFilterSobelHorizBorder_8u16s_C1R_T_Fun(int t, void *arg)
{
    IppStatus status        = ippStsNoErr;

    ippiFilterSobelHV_T_Str* ts = (ippiFilterSobelHV_T_Str *)arg;

    const Ipp8u *pSrc      = (const Ipp8u *)ts->pSrc;
    int srcStep            = ts->srcStep;
    Ipp16s *pDst           = ts->pDst;
    int dstStep            = ts->dstStep;
    IppiMaskSize maskId    = ts->maskId;
    IppiBorderType border  = ts->borderType;
    Ipp8u borderValue      = ts->borderValue;
    Ipp8u *pBuffer         = ts->pBuffer;
    IppiPoint splitImage   = ts->splitImage;
    IppiSize sliceSize     = ts->sliceSize;
    IppiSize lastSliceSize = ts->lastSliceSize;
    int sliceBufferSize    = ts->sliceBufferSize;

    int tWidth  = sliceSize.width;
    int tHeight = sliceSize.height;

    IppiSize roiSize;
    int tx, ty; /* slice coordinates */
    IppiBorderType borderTrd  = border;
    IppiBorderType borderTrdW = borderTrd;

    int threadIdx = 0;
    ippGetThreadIdx_T(&threadIdx);

    ty = t / splitImage.x;
    tx = t % splitImage.x;

    roiSize.height = tHeight;
    if (lastSliceSize.height && (ty == (int)(splitImage.y - 1)))
        roiSize.height = lastSliceSize.height;
    roiSize.width = tWidth;
    if (lastSliceSize.width && (tx == (int)(splitImage.x - 1)))
        roiSize.width = lastSliceSize.width;

    pBuffer = pBuffer + sliceBufferSize * threadIdx;

    Ipp8u*  pSliceSrc = (Ipp8u*) ((Ipp8u*)(pSrc + tx * tWidth) + ty * tHeight * srcStep);
    Ipp16s* pSliceDst = (Ipp16s*)((Ipp8u*)(pDst + tx * tWidth) + ty * tHeight * dstStep);

    if ((splitImage.y > 1))
    {
        if (ty == 0) borderTrd = (IppiBorderType)((int)border | (int)ippBorderInMemBottom);
        else if (ty == (int)(splitImage.y - 1)) borderTrd = (IppiBorderType)((int)border | (int)ippBorderInMemTop);
        else  borderTrd = (IppiBorderType)((int)border | (int)ippBorderInMemBottom | (int)ippBorderInMemTop);
    }
    borderTrdW = borderTrd;
    if ((splitImage.x > 1))
    {
        if (tx == 0) borderTrdW = (IppiBorderType)((int)borderTrd | (int)ippBorderInMemRight);
        else if (tx == (int)(splitImage.x - 1)) borderTrdW = (IppiBorderType)((int)borderTrd | (int)ippBorderInMemLeft);
        else  borderTrdW = (IppiBorderType)((int)borderTrd | (int)ippBorderInMemRight | (int)ippBorderInMemLeft);
    }

    /* Intel IPP function call */
    status = ippiFilterSobelHorizBorder_8u16s_C1R((const Ipp8u*) pSliceSrc, srcStep, (Ipp16s*)pSliceDst, dstStep,
                                                  roiSize, maskId, borderTrdW, borderValue, pBuffer);
    return status;
}

/* /////////////////////////////////////////////////////////////////////////////
//  Name:       ippiFilterSobelHorizBorder_8u16s_C1R_T
//
//  Purpose:    Computes Filter Sobel Horizontal
//
//
//  Parameters:
//   pSrc               Pointer to the source image ROI.
//   srcStep            Distance in bytes between starting points of consecutive lines in the sorce image.
//   pDst               Pointer to the destination image ROI.
//   dstStep            Distance in bytes between starting points of consecutive lines in the destination image.
//   dstRoiSize         Size of destination ROI in pixels.
//   maskId             Predefined mask of IppiMaskSize type.
//   borderType         Type of border.
//   borderValue        Constant value to assign to pixels of the constant border. This parameter is applicable
//                      only to the ippBorderConst border type.
//   pBorderValue       The pointer to constant values to assign to pixels of the constant border. This parameter is applicable
//                      only to the ippBorderConst border type.
//   pBuffer            Pointer to the work buffer.
//
//  Return Values:
//   ippStsNoErr            Indicates no error.
//   ippStsNullPtrErr       Indicates an error when pBufferSize is NULL.
//   ippStsSizeErr          Indicates an error when roiSize is negative, or equal to zero.
//   ippStsNotEvenStepErr   Indicated an error when one of the step values is not divisible by 4
//                          for floating-point images, or by 2 for short-integer images.
//   ippStsBorderErr        Indicates an error when borderType has illegal value.
*/
IPPFUN(IppStatus, ippiFilterSobelHorizBorder_8u16s_C1R_T, (const Ipp8u *pSrc, int srcStep, Ipp16s *pDst, int dstStep, IppiSize dstRoiSize,
                                                          IppiMaskSize maskId, IppiBorderType borderType, Ipp8u borderValue, Ipp8u* pBuffer))
{
    IppStatus status = ippStsNoErr;

    if (pSrc == 0 || pDst == 0 || pBuffer == 0) return ippStsNullPtrErr;
    if (dstRoiSize.height <=0 || dstRoiSize.width <=0) return ippStsSizeErr;
    if (maskId != ippMskSize3x3 && maskId != ippMskSize5x5) return ippStsMaskSizeErr;

    int numChannels   = 1;
    Ipp32u numThreads = 1;

    ippGetNumThreads_T((int*)&numThreads);

    if (numThreads == 1)
    {
        status = ippiFilterSobelHorizBorder_8u16s_C1R(pSrc, srcStep, pDst, dstStep, dstRoiSize, maskId, borderType, borderValue, pBuffer);
    }
    else
    {
        IppiSize sliceSize, lastSliceSize;
        IppiPoint splitImage;

        ownGetSobelSliceSize(dstRoiSize, maskId, &sliceSize, &lastSliceSize, &splitImage);

        int bufferSize = 0;
        status = ippiFilterSobelHorizBorderGetBufferSize(lastSliceSize, maskId, ipp8u, ipp16s, numChannels, &bufferSize);

        if (status >= 0)
        {
            int numTiles = splitImage.x * splitImage.y;

            ippiFilterSobelHV_T_Str ts;
            filterSobelHVThreadingStructureEncode_8u16s((Ipp8u*)pSrc, srcStep, pDst, dstStep, maskId, borderType, borderValue, pBuffer, splitImage,
                                                        sliceSize, lastSliceSize, bufferSize, &ts);
            status = ippParallelFor_T(numTiles, (void*)&ts, ippiFilterSobelHorizBorder_8u16s_C1R_T_Fun);
        }
    }

    return status;
}
