/*******************************************************************************
* Copyright 2018-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 <math.h>
#include <memory>

#include "base.h"
#include "base_image.h"
#include "base_renderer.h"
#include "base_ipp.h"

#include "ippcore_tl.h"
#include "ipps.h"

#if defined USE_OMP
#include <omp.h>
#elif defined USE_TBB
#define TBB_PREVIEW_GLOBAL_CONTROL 1
#include "tbb/global_control.h"
#endif

/* Sobel Filter parameters structure to use in Threading Layer's parallel_for() function */

typedef struct _FilterSobel_T_Str
{
	const Ipp8u* pSrc;
	int srcStep;
	Ipp16s* pDst;
	int dstStep;
	IppiMaskSize maskId;        /* Sobel Filter mask size                                                   */      
	IppiBorderType borderType;  /* Type of border                                                           */
	Ipp8u borderValue;          /* Border value in case of constant border                                  */
	Ipp8u* pBuffer;             /* Pointer to the calculations buffer                                       */
    int slicesCount;
	int sliceBufferSize;        /* Calculations buffer size for single slice                                */
	int intermediateBufferSize; /* Intermediate results storage for functions in the pipeline               */
	int lineBufferSize;         /* Intermediate results storage for accurate calculations in the pipeline   */
	IppiSize sliceSize;
	IppiSize lastSliceSize;
} FilterSobel_T_Str;

static void printVersion()
{
    const IppLibraryVersion *pVer = ippsGetLibVersion();

    printf("\nIntel(R) IPP Threading Layers Example: Sobel Filter (application level parallelism)");
    printf("\nIt demonstrates application level parallelism approach implemented for Sobel edge-detector filter pipeline.");
    printf("\nIt splits an image on slices and then runs full Sobel Filter pipeline for every slice.");
    printf("\nEach slice is processed in a separate thread.\n");
    printf("\nBased on:");

    IppThreadingType thrType;
    ippGetThreadingType_LT (&thrType);
    printf("\nIntel(R) IPP Threading Layer (%s)", (thrType==OMP)? "OpenMP" : "TBB");

    printf("\nIntel(R) IPP: %s %s %s", pVer->Name, pVer->Version, pVer->BuildDate);
    printf("\n");
}

static void printHelp(const cmd::OptDef pOptions[], char* argv[])
{
    printf("\nUsage: %s [-i] InputFile [[-o] OutputFile] [Options]\n", GetProgName(argv));
    printf("Options:\n");
    cmd::OptUsage(pOptions);
}

/* /////////////////////////////////////////////////////////////////////////////
//  Name:               splitImageToSlices
//
//  Purpose:            Splits image to equal slices depending on ROI size and Sobel mask size.
//                      Last slice can be slightly bigger than others.
//
//  Parameters:
//
//   [in]  roiSize            Size of destination ROI in pixels.
//   [in]  maskHeigh          Height of Sobel mask
//   [out] pTileSize          Size of single slice
//   [out] lastTileSize       Size of last slice
//   [out] slicesCount        Count of slices
//
//  Return Values:
//   void
*/
static void splitImageToSlices(IppiSize roiSize, int maskHeight, IppiSize* const pSliceSize, IppiSize* const pLastSliceSize, int* const slicesCount);


/* /////////////////////////////////////////////////////////////////////////////
//  Name:               ippiFilterSobel_8u16s_C1R_T_Fun
//
//  Purpose:            Kernel to be called in ippParallelFor_T() of Threading Layer -
//                      it runs Filter Sobel functions pipeline for particular slice of the image.
//                      The pipeline includes:
//
//                         ippiFilterSobelHorizBorder_8u16s_C1R
//                         ippiFilterSobelVertBorder_8u16s_C1R
//                         ippsMul_16s_ISfs
//                         ippiMul_16s_C1IRSfs
//                         ippiAdd_16s_C1IRSfs
//                         ippiSqrt_16s_C1IRSfs
//
//  Parameters:
//   [in] t                thread index
//   [in] arg              pointer to the Filter Sobel threading structure encoding function parameters arguments
//
//  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.
//   ippStsSqrtNegArg       Indicates that source image pixel has a negative value
*/
static IppStatus ippiFilterSobel_8u16s_C1R_T_Fun(int t, void * const arg);

/* /////////////////////////////////////////////////////////////////////////////
//  Name:               filterSobelThreadingStructureEncode_8u16s
//
//  Purpose:            Initialization function for FilterSobel_T_Str structure.
//
//  Parameters:
//   [in]  pSrc                         Pointer to the source array
//   [in]  srcStep                      Source array step
//   [in]  pDst                         Pointer to the destination array
//   [in]  dstStep                      Destination array step
//   [in]  maskId                       Sobel mask type (ippMskSize3x3, ippMskSize5x5)
//   [in]  borderType                   Border type
//   [in]  borderValue                  Border value for the case of ippBorderConst border
//   [in]  pBuffer                      Pointer to the calculations buffer
//   [in]  slicesCount                  Count of slices the image is splitted on
//   [in]  sliceBufferSize              Calculations buffer size for single slice
//   [in]  intermediateBufferSize       Size of intermediate results storage for functions in the pipeline
//   [in]  sliceSize                    Size of single slice
//   [in]  lastSliceSize                Size of last slice
//   [out] ts                           FilterSobel_T_Str structure to be initialized
//
//  Return Values:
//      void
*/
static void filterSobelThreadingStructureEncode_8u16s(
    const Ipp8u* pSrc,
    int srcStep,
    Ipp16s* pDst,
    int dstStep,
    IppiMaskSize maskId,
    IppiBorderType borderType,
    Ipp8u borderValue,
    Ipp8u* pBuffer,
    int slicesCount,
    int sliceBufferSize,
    int intermediateBufferSize,
    int lineBufferSize,
    IppiSize sliceSize,
    IppiSize lastSliceSize,
    FilterSobel_T_Str* ts
);

int main(int argc, char *argv[])
{
    /*
    // Variables initialization
    */
    Status       status          = STS_OK;
    DString      sInputFile      = CheckTestDirs( BMP_GRAYSCALE_FILE );
    DString      sOutputFile;
    DString      sIppCpu;

    unsigned int  threads        = 0;
    bool          bPrintHelp     = false;

    Image srcData;
    Image dstData;

    // General timing
    vm_tick      tickStart   = 0;
    vm_tick      tickAcc     = 0;
    vm_tick      tickFreq    = vm_time_get_frequency()/1000;
    double       fTime       = 0;
    double       fTimeLimit  = 0;
    unsigned int iLoops      = 0;
    unsigned int iLoopsLimit = 0;

    /*
    // Cmd parsing
    */
    const cmd::OptDef cmdOpts[] =
    {
        { 'i', "", 1, cmd::KT_DSTRING,   cmd::KF_OPTIONAL,  &sInputFile,      "input file name" },
        { 'o', "", 1, cmd::KT_DSTRING,   cmd::KF_OPTIONAL,  &sOutputFile,     "output file name" },
#if defined USE_OMP
        { 't', "", 1, cmd::KT_INTEGER,   0,                 &threads,         "number of threads for TL interface (OpenMP)" },
#elif defined USE_TBB
        { 't', "", 1, cmd::KT_INTEGER,   0,                 &threads,         "number of tasks for TL interface (TBB)" },
#endif
        { 'w', "", 1, cmd::KT_DOUBLE,    0,                 &fTimeLimit,      "minimum test time in milliseconds" },
        { 'l', "", 1, cmd::KT_POSITIVE,  0,                 &iLoopsLimit,     "number of loops (overrides test time)" },
        { 'T', "", 1, cmd::KT_DSTRING,   0,                 &sIppCpu,         "target Intel IPP optimization (" IPP_OPT_LIST ")" },
        { 'h', "", 1, cmd::KT_BOOL,      0,                 &bPrintHelp,      "print help and exit" },
        {0}
    };

    if (cmd::OptParse(argc, argv, cmdOpts))
    {
        printHelp(cmdOpts, argv);
        PRINT_MESSAGE("Invalid input parameters");
        return 1;
    }

    InitPreferredCpu(sIppCpu.c_str());
	
    // Check default image availability
    if ( !strcmp(sInputFile.c_str(), BMP_GRAYSCALE_FILE) ) {
        bPrintHelp = ( -1 == vm_file_access(sInputFile.c_str(), 0) );
    }

    if(bPrintHelp)
    {
        printHelp(cmdOpts, argv);
        return 0;
    }

    if(!sInputFile.Size())
    {
        printHelp(cmdOpts, argv);
        PRINT_MESSAGE("Cannot open input file");
        return 1;
    }

    printVersion();

    while(1)
    {
        IppStatus       ippStatus;
        IppiSize        ippSrcSize;
        IppDataType     ippSrcType;
        IppDataType     ippDstType;
        int             ippDstTypeSize  = sizeof(Ipp16s);
        IppiMaskSize    sobelMaskId     = ippMskSize3x3;
        int             sobelMaskHeight = 3;
        IppiBorderType  borderType      = (IppiBorderType)(ippBorderConst);
        Ipp8u           borderValue     = 0;
        IppNormType     normType        = ippNormL2;

        int cmnBufferSize = 0;
        int bufferSizeH   = 0;
        int bufferSizeV   = 0;

        IppiSize sliceSize;
        IppiSize lastSliceSize;
        int slicesCount = 0;

        AutoBuffer<Ipp8u> ippFSobelTmpBuffer;

        // Read from file
        printf("\nInput file: %s\n", sInputFile.c_str());
        status = srcData.Read(sInputFile, CF_GRAY, ST_8U);
        CHECK_STATUS_PRINT_BR(status, "Image::Read()", GetBaseStatusString(status));
        printf("Input info: %dx%d %s\n", (int)srcData.m_size.width, (int)srcData.m_size.height, colorFormatName[srcData.m_color]);

        if (!threads) threads = 1;

#if defined USE_OMP
        omp_set_num_threads (threads);
#elif defined USE_TBB
		tbb::global_control set_num_threads(tbb::global_control::max_allowed_parallelism, threads); // set_num_threads(threads)
#endif
        status = dstData.Alloc(srcData.m_size, srcData.m_color, ST_16S);
        CHECK_STATUS_PRINT_BR(status, "dstData.Alloc", GetBaseStatusString(status));

        printf("\nOutput file: %s\n", (sOutputFile.Size())?sOutputFile.c_str():"-");
        printf("Output info: %dx%d %s\n\n", (int)dstData.m_size.width, (int)dstData.m_size.height, colorFormatName[dstData.m_color]);

        ippSrcSize = ImageSizeToIppOld(srcData.m_size);
        ippSrcType = ImageFormatToIpp(srcData.m_sampleFormat);
        ippDstType = ImageFormatToIpp(dstData.m_sampleFormat);

        // Divide image on slices
        splitImageToSlices(ippSrcSize, sobelMaskHeight, &sliceSize, &lastSliceSize, &slicesCount);

        // Get size of slice buffer to store intermediate results of the calculations in the pipeline
        IppiSize maxSliceSize = { ippSrcSize.width, (sliceSize.height > lastSliceSize.height ? sliceSize.height : lastSliceSize.height) };

        // Get slice buffer sizes for Filter Sobel Horizontal and Filter Sobel Vertical
        ippStatus = ippiFilterSobelHorizBorderGetBufferSize(maxSliceSize, sobelMaskId, ippSrcType, ippDstType, 1 /* channels */, &bufferSizeH);
        CHECK_STATUS_PRINT_BR(ippStatus, "ippiFilterSobelHorizBorderGetBufferSize()", ippGetStatusString(ippStatus));

        ippStatus = ippiFilterSobelVertBorderGetBufferSize(maxSliceSize, sobelMaskId, ippSrcType, ippDstType, 1 /* channels */, &bufferSizeV);
        CHECK_STATUS_PRINT_BR(ippStatus, "ippiFilterSobelVertBorderGetBufferSize()", ippGetStatusString(ippStatus));

        // Common buffer size is maximum of Horizontal and Vertical buffers
        cmnBufferSize = (bufferSizeH > bufferSizeV ? bufferSizeH : bufferSizeV);

        // Allocate general buffers for calculations that aggregates separate buffers for each thread.
        // Each thread buffer consists of two parts: buffers of intermediate storage of pipeline functions results and
        // buffer for temporary calculations. See buffers scheme in the comments of ippiFilterSobel_8u16s_C1R_T_Fun function below.
        int sliceBufferSize = cmnBufferSize + maxSliceSize.height * maxSliceSize.width * ippDstTypeSize + 4 * maxSliceSize.width * ippDstTypeSize;
        ippFSobelTmpBuffer.Alloc(sliceBufferSize * threads);

        printf("API:       Classic API with Manual Parallel Processing\n");
        printf("Type:      Pipeline per Slice\n");
        printf("Threads:   %u\n",    threads);

        for(iLoops = 1, tickAcc = 0;; iLoops++)
        {
            // Encode Sobel functions parameters in a structure to pass it into ippParallelFor_T()
            FilterSobel_T_Str ts;
            filterSobelThreadingStructureEncode_8u16s(
                (Ipp8u*)srcData.ptr(), 
                srcData.m_step, 
                (Ipp16s*)dstData.ptr(), 
                dstData.m_step, 
                sobelMaskId,
                borderType, 
                borderValue, 
                ippFSobelTmpBuffer, 
                slicesCount, 
                sliceBufferSize,
                maxSliceSize.height * maxSliceSize.width * ippDstTypeSize,
                2 * maxSliceSize.width * ippDstTypeSize,
                sliceSize, 
                lastSliceSize, 
                &ts);

            tickStart = vm_time_get_tick();

            // Run function that implements Filter Sobel pipeline using parallelFor_T() helper. Such helper allows to run
            // function on multiple threads. Each thread processes part of the image (slices). The number of the threads is
            // specified earlier by set_num_threads().
            ippStatus = ippParallelFor_T(slicesCount, (void*)&ts, ippiFilterSobel_8u16s_C1R_T_Fun);
            CHECK_STATUS_PRINT_BR(ippStatus, "ippiFilterSobel_8u16s_C1R_T_Fun", ippGetStatusString(ippStatus));

            tickAcc += (vm_time_get_tick() - tickStart);

            fTime = (double)tickAcc/tickFreq;
            if(iLoopsLimit)
            {
                if(iLoops >= iLoopsLimit)
                    break;
            }
            else
            {
                if(fTime >= fTimeLimit)
                    break;
            }
        }
        if(status < 0) break;

        /*
        // Results output
        */
        printf("\nLoops:      %u\n", iLoops);
        printf("Time total: %0.3fms\n", fTime);
        printf("Loop avg:   %0.3fms\n", fTime/iLoops);

        if(sOutputFile.Size())
        {
            Image dstData_8u;
            status = dstData_8u.Alloc(dstData.m_size, dstData.m_color, ST_8U);

            Ipp16s* s = (Ipp16s*)dstData.ptr();
            Ipp8u*  d = (Ipp8u*) dstData_8u.ptr();
            for(int i = 0; i < dstData.m_size.width * dstData.m_size.height; i++)
            {
                d[i] = (s[i] < 0 ? 0 : (s[i] > 255 ? 255 : (Ipp8u)s[i]));
            }
            status = dstData_8u.Write(sOutputFile.c_str());
            CHECK_STATUS_PRINT_BR(status, "Image::Write()", GetBaseStatusString(status));
        }

        break;
    }

    if(status < 0)
        return status;

    return 0;
}

static void splitImageToSlices(IppiSize roiSize, int maskHeight, IppiSize* const pSliceSize, IppiSize* const pLastSliceSize, int* const slicesCount)
{
    int widthImage  = roiSize.width;
    int heightImage = roiSize.height;

    // Minimal slice height depends on filter mask size
    int heightSlice     = (maskHeight + 1 < heightImage ? maskHeight + 1 : heightImage);
    // Height of last slice can be slightly bigger than other slices height if image height is not factor of slice height.
    int heightLastSlice = heightImage % heightSlice;

    (*pSliceSize).width  = widthImage;
    (*pSliceSize).height = heightSlice;

    (*pLastSliceSize).width  = widthImage;
    (*pLastSliceSize).height = (heightLastSlice + heightSlice);

    *slicesCount = (int) (heightImage / heightSlice);
}

static IppStatus ippiFilterSobel_8u16s_C1R_T_Fun(int t, void * const arg)
{
    IppStatus status = ippStsNoErr;

    // Void pointer passed to this kernel function is a pointer to parameters structure
    FilterSobel_T_Str * ts = (FilterSobel_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 borderType = ts->borderType;
    Ipp8u borderValue = ts->borderValue;
    Ipp8u *pBuffer = ts->pBuffer;
    int sliceBufferSize = ts->sliceBufferSize;
    int intermediateBufferSize = ts->intermediateBufferSize;
    int lineBufferSize = ts->lineBufferSize;
    int slicesCount = ts->slicesCount;
    IppiSize sliceSize = ts->sliceSize;
    IppiSize lastSliceSize = ts->lastSliceSize;

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

    IppiSize roiSize;
    // Slice Y-coordinate
    int ty;

    IppiBorderType borderTrd = borderType;

    // Get current thread index
    int threadIdx = 0;
    ippGetThreadIdx_T(&threadIdx);

    // Calculate slice coordinates to process in the current thread
    ty = t;

    // Define ROI size depending on slice size
    roiSize.height = tHeight;
    if (lastSliceSize.height && (ty == (int)(slicesCount - 1)))
        roiSize.height = lastSliceSize.height;
    roiSize.width = tWidth;

    /* Pointer to the beginning of calculations buffer for the current slice.
    // It consists of two parts: buffer of intermediate storage of pipeline functions results and
    // buffer for temporary calculations of pipeline functions.
    //
    //  buffer1..N      - separate buffers for each thread (size is sliceBufferSize)
    //  pBuffer         - pointer to the beginning of general buffer
    //  pSliceBuffer    - pointer to the beginning of calculation buffer of the current slice
    //  pTmpBuffer      - pointer to the beginning of buffer for temporary calculations of the current slice
    //
    // Example for the slice2:
    //
    //                                         General buffer (ippFSobelTmpBuffer)
    //
    //   ______________________________________________/\_______________________________________________
    //  /                                                                                               \
    //  _________________________________________________________________________________________________
    //  |___________|___________|-----------|--|--|------|___________|___________|___________|___________|
    //
    //  \__________  __________/ \__________  __________/           ...          \__________  __________/
    //             \/                       \/                                              \/
    //
    //         buffer1                   buffer2                                         bufferN
    //
    //  ^                       ^           ^
    //  |                       |           |
    // pBuffer                  |           |
    //                       pSliceBuffer   |
    //                                      |
    //                                  pTmpBuffer
    //
    //
    //   where N - number of threads
    //
    */

    // Pointer to the start of the calculations buffer for the current slice
    Ipp8u *pSliceBuffer = pBuffer + sliceBufferSize * threadIdx;
    // Pointers to the temporary calculations buffers within slice buffer
    Ipp8u* pTmpBuffer = pSliceBuffer + intermediateBufferSize;
    Ipp8u* pFilSobelBuffer = pTmpBuffer + 2 * lineBufferSize;
    // Pointer to the intermediate results buffer within slice buffer
    Ipp16s *pSliceDstIntermediate = (Ipp16s*)pSliceBuffer;

    // Pointer to the start of the current slice in the source array
    Ipp8u*  pSliceSrc = (Ipp8u*)((Ipp8u*)pSrc + ty * tHeight * srcStep);
    // Pointer to the start of the current slice in the destination array
    Ipp16s* pSliceDst = (Ipp16s*)((Ipp8u*)pDst + ty * tHeight * dstStep);

    // Borders processing
    if (slicesCount > 1)
    {
        if (ty == 0) borderTrd = (IppiBorderType)((int)borderType | (int)ippBorderInMemBottom);
        else if (ty == (int)(slicesCount - 1)) borderTrd = (IppiBorderType)((int)borderType | (int)ippBorderInMemTop);
        else  borderTrd = (IppiBorderType)((int)borderType | (int)ippBorderInMemBottom | (int)ippBorderInMemTop);
    }

    // This can be not equal to dstStep if we use filter on the part of an image
    int dstStepIntermediate = sliceSize.width * sizeof(*pDst);

    /* Intel IPP function calls */
    // 1. Sobel filter Horizontal (Classic API function applied to one slice)
    status = ippiFilterSobelHorizBorder_8u16s_C1R((const Ipp8u*)pSliceSrc, srcStep, (Ipp16s*)pSliceDstIntermediate, dstStepIntermediate,
        roiSize, maskId, borderTrd, borderValue, pFilSobelBuffer);

    if (status != ippStsNoErr) return status;

    // 2. Sobel filter Vertical (Classic API function applied to one slice)
    status = ippiFilterSobelVertBorder_8u16s_C1R((const Ipp8u*)pSliceSrc, srcStep, (Ipp16s*)pSliceDst, dstStep,
        roiSize, maskId, borderTrd, borderValue, pFilSobelBuffer);

    if (status != ippStsNoErr) return status;

	// Processing each slice line by line
    Ipp16s *pLineDst = 0;
    Ipp16s *pLineDstIntermediate = 0;
	// Pointers to the start of buffers for intermediate 32s data.
    Ipp32s *pLineDst_32s = (Ipp32s *)(pSliceBuffer + intermediateBufferSize);
    Ipp32s *pLineDstIntermediate_32s = (Ipp32s *)(pSliceBuffer + intermediateBufferSize + lineBufferSize);
    for (int i = 0; i < roiSize.height; ++i) {
		// Getting line from the slice 
        pLineDst = (Ipp16s *)((Ipp8u *)pSliceDst + i * dstStep);
        pLineDstIntermediate = (Ipp16s *)((Ipp8u *)pSliceDstIntermediate + i * dstStepIntermediate);

        // 3. Sqr of Sobel filter Horizontal values (Classic API function applied to one line)
        status = ippsMul_16s32s_Sfs((const Ipp16s *)pLineDstIntermediate, (const Ipp16s *)pLineDstIntermediate, (Ipp32s *)pLineDstIntermediate_32s, roiSize.width, 0 /*scale*/);
        if (status != ippStsNoErr) return status;

        // 4. Sqr of Sobel filter Vertical values (Classic API function applied to one line)
        status = ippsMul_16s32s_Sfs((const Ipp16s *)pLineDst, (const Ipp16s *)pLineDst, (Ipp32s *)pLineDst_32s, roiSize.width, 0 /*scale*/);
        if (status != ippStsNoErr) return status;

        // 5. Addition of calculations from item 3. and 4. (Classic API function applied to one line)
        status = ippsAdd_32s_Sfs((const Ipp32s *)pLineDstIntermediate_32s, (const Ipp32s *)pLineDst_32s, (Ipp32s *)pLineDst_32s, roiSize.width, 0 /*scale*/);
        if (status != ippStsNoErr) return status;

        // 6. Square root of calculations from item 5. (Classic API function applied to one line)
        status = ippsSqrt_32s16s_Sfs((const Ipp32s *)pLineDst_32s, (Ipp16s *)pLineDst, roiSize.width, 0 /*scale*/);
        if (status != ippStsNoErr) return status;
    }

    return status;
}

/* Initialize Sobel Filter threading structure */
static void filterSobelThreadingStructureEncode_8u16s(
    const Ipp8u* pSrc,
    int srcStep,
    Ipp16s* pDst,
    int dstStep,
    IppiMaskSize maskId,
    IppiBorderType borderType,
    Ipp8u borderValue,
    Ipp8u* pBuffer,
    int slicesCount,
    int sliceBufferSize,
    int intermediateBufferSize,
    int lineBufferSize,
    IppiSize sliceSize,
    IppiSize lastSliceSize,
    FilterSobel_T_Str* ts
)
{
    ts->pSrc = pSrc;
    ts->srcStep = srcStep;
    ts->pDst = pDst;
    ts->dstStep = dstStep;
    ts->maskId = maskId;
    ts->borderType = borderType;
    ts->borderValue = borderValue;
    ts->pBuffer = pBuffer;
    ts->slicesCount = slicesCount;
    ts->sliceBufferSize = sliceBufferSize;
    ts->intermediateBufferSize = intermediateBufferSize;
    ts->lineBufferSize = lineBufferSize;
    ts->sliceSize = sliceSize;
    ts->lastSliceSize = lastSliceSize;
}
