/*******************************************************************************
* Copyright 2012-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_ipp.h"
#include "base_renderer.h"

#include "ippcore.h"
#include "ipps.h"
#include "ippi.h"

#ifdef USE_TBB
//#define __TBB_NO_IMPLICIT_LINKAGE 0
#define TBB_PREVIEW_MEMORY_POOL 1
#include "tbb/task_arena.h"
#include "tbb/parallel_for.h"
#include "tbb/blocked_range2d.h"
#include "tbb/memory_pool.h"
#define TBB_PREVIEW_GLOBAL_CONTROL 1
#include "tbb/global_control.h"

using namespace tbb;
#endif

#define CMD_INTER_NN  0
#define CMD_INTER_LIN 1
#define CMD_INTER_CUB 2
#define CMD_INTER_LAN 3


static void printVersion()
{
    const IppLibraryVersion *pVersion;
    printf("\nIntel(R) IPP:\n");
    PRINT_LIB_VERSION(  , pVersion)
    PRINT_LIB_VERSION(s,  pVersion)
    PRINT_LIB_VERSION(i,  pVersion)
}

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);
}

class Resize
{
public:
    Resize()
    {
        m_iThreads = 0;

        m_interpolation = ippLinear;
        m_pSpec         = 0;
        m_pInitBuffer   = 0;

        m_fBVal   = 1;
        m_fCVal   = 0;
        m_iLobes  = 3;
    }

    virtual ~Resize()
    {
        Close();
    }

    void Close()
    {
        if(m_pSpec)
        {
            ippsFree(m_pSpec);
            m_pSpec = 0;
        }

        if(m_pInitBuffer)
        {
            ippsFree(m_pInitBuffer);
            m_pInitBuffer = 0;
        }
    }

    virtual Status Init(Image *pSrcImage, Image *pDstImage)
    {
        IppStatus       ippSts;
        IppiBorderSize  borderSize;
        int             iSpecSize = 0;
        int             iInitSize = 0;

        if(!pSrcImage || !pSrcImage->ptr() || !pDstImage)
            return STS_ERR_NULL_PTR;

        if(pSrcImage->m_samples != 1 && pSrcImage->m_samples != 3 && pSrcImage->m_samples != 4)
            return STS_ERR_INVALID_PARAMS;

        Close();

        IppiSize srcSize = {static_cast<int>(pSrcImage->m_size.width), static_cast<int>(pSrcImage->m_size.height)};
        IppiSize dstSize = {static_cast<int>(pDstImage->m_size.width), static_cast<int>(pDstImage->m_size.height)};

        // Get sizes for internal and initialization buffers
        ippSts = ippiResizeGetSize_8u(srcSize, dstSize, m_interpolation, 0, &iSpecSize, &iInitSize);
        CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeGetSize_8u()", ippGetStatusString(ippSts), return STS_ERR_FAILED);

        // allocate internal buffer
        m_pSpec = (IppiResizeSpec_32f*)ippsMalloc_8u(iSpecSize);
        if(!m_pSpec)
        {
            PRINT_MESSAGE("Cannot allocate memory for resize spec");
            return STS_ERR_ALLOC;
        }

        // allocate initialization buffer
        if(iInitSize)
        {
            m_pInitBuffer = ippsMalloc_8u(iInitSize);
            if(!m_pInitBuffer)
            {
                PRINT_MESSAGE("Cannot allocate memory for resize init buffer");
                return STS_ERR_ALLOC;
            }
        }

        // init ipp resizer
        if(m_interpolation == ippNearest)
        {
            ippSts = ippiResizeNearestInit_8u(srcSize, dstSize, m_pSpec);
            CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeNearestInit_8u()", ippGetStatusString(ippSts), return STS_ERR_FAILED);
        }
        else if(m_interpolation == ippLinear)
        {
            ippSts = ippiResizeLinearInit_8u(srcSize, dstSize, m_pSpec);
            CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeLinearInit_8u()", ippGetStatusString(ippSts), return STS_ERR_FAILED);
        }
        else if(m_interpolation == ippCubic)
        {
            ippSts = ippiResizeCubicInit_8u(srcSize, dstSize, m_fBVal, m_fCVal, m_pSpec, m_pInitBuffer);
            CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeCubicInit_8u()", ippGetStatusString(ippSts), return STS_ERR_FAILED);
        }
        else if(m_interpolation == ippLanczos)
        {
            ippSts = ippiResizeLanczosInit_8u(srcSize, dstSize, m_iLobes, m_pSpec, m_pInitBuffer);
            CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeLanczosInit_8u()", ippGetStatusString(ippSts), return STS_ERR_FAILED);
        }

        ippSts = ippiResizeGetBorderSize_8u(m_pSpec, &borderSize);
        CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeGetBorderSize_8u()", ippGetStatusString(ippSts), return STS_ERR_FAILED);

        m_templ = *pSrcImage;

        return STS_OK;
    }

    Status ResizeBlock(Image *pSrcImage, Image *pDstImage, Rect roi, IppiBorderType border, unsigned char *pExtBuffer = 0)
    {
        Status      status;
        IppStatus   ippSts;
        IppiPoint   dstRoiOffset = {(int)roi.x, (int)roi.y};
        IppiSize    dstRoiSize   = {(int)roi.width, (int)roi.height};
        IppiPoint   srcRoiOffset;
        IppiSize    srcRoiSize;

        unsigned char *pSrcPtr = 0;
        unsigned char *pDstPtr = 0;
        unsigned char *pBuffer = 0;
        int            iBufferSize = 0;

        if(!pSrcImage || !pDstImage)
            return STS_ERR_NULL_PTR;

        if(!m_pSpec)
            return STS_ERR_NOT_INITIALIZED;

        // Zero size mean full size
        if(!dstRoiSize.width)
            dstRoiSize.width  = (int)pDstImage->m_size.width;
        if(!dstRoiSize.height)
            dstRoiSize.height = (int)pDstImage->m_size.height;

        if(m_templ != *pSrcImage)
        {
            status = Init(pSrcImage, pDstImage);
            CHECK_STATUS_PRINT_RS(status, "Resize::Init()", GetBaseStatusString(status));
        }

        // get src ROI from dst ROI
        ippSts = ippiResizeGetSrcRoi_8u(m_pSpec, dstRoiOffset, dstRoiSize, &srcRoiOffset, &srcRoiSize);
        CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeGetSrcRoi_8u()", ippGetStatusString(ippSts), return STS_ERR_FAILED);

        // adjust input and output buffers to current ROI
        pSrcPtr = (unsigned char*)pSrcImage->ptr(srcRoiOffset.y, srcRoiOffset.x);
        pDstPtr = (unsigned char*)pDstImage->ptr(dstRoiOffset.y, dstRoiOffset.x);

        if(!pExtBuffer)
        {
            ippSts = ippiResizeGetBufferSize_8u(m_pSpec, dstRoiSize, pSrcImage->m_samples, &iBufferSize);
            CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeGetBufferSize_8u()", ippGetStatusString(ippSts), return STS_ERR_FAILED);

            pBuffer = ippsMalloc_8u(iBufferSize);
            if(!pBuffer)
            {
                PRINT_MESSAGE("Cannot allocate memory for resize buffer");
                return STS_ERR_ALLOC;
            }
        }
        else
            pBuffer = pExtBuffer;

        // perform resize
        if(m_interpolation == ippNearest)
        {
            if(pSrcImage->m_samples == 1)
                ippSts = ippiResizeNearest_8u_C1R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, m_pSpec, pBuffer);
            else if(pSrcImage->m_samples == 3)
                ippSts = ippiResizeNearest_8u_C3R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, m_pSpec, pBuffer);
            else if(pSrcImage->m_samples == 4)
                ippSts = ippiResizeNearest_8u_C4R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, m_pSpec, pBuffer);
        }
        else if(m_interpolation == ippLinear)
        {
            if(pSrcImage->m_samples == 1)
                ippSts = ippiResizeLinear_8u_C1R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, border, 0, m_pSpec, pBuffer);
            else if(pSrcImage->m_samples == 3)
                ippSts = ippiResizeLinear_8u_C3R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, border, 0, m_pSpec, pBuffer);
            else if(pSrcImage->m_samples == 4)
                ippSts = ippiResizeLinear_8u_C4R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, border, 0, m_pSpec, pBuffer);
        }
        else if(m_interpolation == ippCubic)
        {
            if(pSrcImage->m_samples == 1)
                ippSts = ippiResizeCubic_8u_C1R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, border, 0, m_pSpec, pBuffer);
            else if(pSrcImage->m_samples == 3)
                ippSts = ippiResizeCubic_8u_C3R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, border, 0, m_pSpec, pBuffer);
            else if(pSrcImage->m_samples == 4)
                ippSts = ippiResizeCubic_8u_C4R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, border, 0, m_pSpec, pBuffer);
        }
        else if(m_interpolation == ippLanczos)
        {
            if(pSrcImage->m_samples == 1)
                ippSts = ippiResizeLanczos_8u_C1R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, border, 0, m_pSpec, pBuffer);
            else if(pSrcImage->m_samples == 3)
                ippSts = ippiResizeLanczos_8u_C3R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, border, 0, m_pSpec, pBuffer);
            else if(pSrcImage->m_samples == 4)
                ippSts = ippiResizeLanczos_8u_C4R(pSrcPtr, (int)pSrcImage->m_step, pDstPtr, (int)pDstImage->m_step, dstRoiOffset, dstRoiSize, border, 0, m_pSpec, pBuffer);
        }
        CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeX_8u_CXR()", ippGetStatusString(ippSts), return STS_ERR_FAILED);

        if(!pExtBuffer)
            ippsFree(pBuffer);

        return STS_OK;
    }

    virtual Status ResizeImage(Image *pSrcImage, Image *pDstImage)
    {
        if(!pSrcImage || !pDstImage)
            return STS_ERR_NULL_PTR;

        Rect roi(pDstImage->m_size.width, pDstImage->m_size.height);

        return ResizeBlock(pSrcImage, pDstImage, roi, ippBorderRepl);
    }

public:
    unsigned int m_iThreads;

    IppiInterpolationType m_interpolation;
    float        m_fBVal;
    float        m_fCVal;
    unsigned int m_iLobes;

protected:
    Image m_templ;

    IppiResizeSpec_32f *m_pSpec;
    unsigned char      *m_pInitBuffer;
};

#ifdef USE_TBB
class ResizeTBB : public Resize
{
public:
    ResizeTBB()
    {
        m_iGrainX  = 0;
        m_iGrainY  = 0;
    }

    Status Init(Image *pSrcImage, Image *pDstImage)
    {
        Status status;

        status = Resize::Init(pSrcImage, pDstImage);
        CHECK_STATUS_PRINT_RS(status, "Resize::Init()", GetBaseStatusString(status));

        if(m_iThreads == 0) // automatic threads number
        {
#if TBB_INTERFACE_VERSION >= 9100
			m_iThreads = this_task_arena::max_concurrency();
#else
			m_iThreads = task_arena::max_concurrency();
#endif
        }

        if(!m_iGrainX)
            m_iGrainX = (unsigned int)(pDstImage->m_size.width + m_iThreads - 1)/m_iThreads;

        if(!m_iGrainY)
            m_iGrainY = (unsigned int)(pDstImage->m_size.height + m_iThreads - 1)/m_iThreads;

        m_task.m_pSrcData = pSrcImage;
        m_task.m_pDstData = pDstImage;
        m_task.m_pResize  = this;

        return STS_OK;
    }

    Status ResizeImage(Image *pSrcImage, Image *pDstImage)
    {
        if(!pSrcImage || !pDstImage)
            return STS_ERR_NULL_PTR;

        if(m_templ != *pSrcImage)
        {
            Status status = Init(pSrcImage, pDstImage);
            CHECK_STATUS_PRINT_RS(status, "Resize::Init()", GetBaseStatusString(status));
        }

        blocked_range2d<unsigned int, unsigned int> tbbRange(0, (unsigned int)pDstImage->m_size.height, m_iGrainY, 0, (unsigned int)pDstImage->m_size.width, m_iGrainX);

        try
        {
			global_control set_num_threads(global_control::max_allowed_parallelism, m_iThreads);
			parallel_for(tbbRange, m_task, m_part_auto);
        }
        catch(Status status)
        {
            return status;
        }

        return STS_OK;
    }

private:
    class ResizeTBBTask
    {
    public:
        ResizeTBBTask() {};

        void operator()(blocked_range2d<unsigned int, unsigned int> &r) const
        {
            IppStatus ippSts;
            Status    status;
            Rect      roi( (int)r.cols().begin(), (int)r.rows().begin(), r.cols().end() - r.cols().begin(), r.rows().end() - r.rows().begin() );
            IppiSize  dstRoiSize  = {(int)roi.width, (int)roi.height};
            IppiBorderType border = ippBorderRepl;

            unsigned char *pBuffer = 0;
            int iBufferSize;

            ippSts = ippiResizeGetBufferSize_8u(m_pResize->m_pSpec, dstRoiSize, m_pSrcData->m_samples, &iBufferSize);
            CHECK_STATUS_PRINT_AC(ippSts, "ippiResizeGetBufferSize_8u()", ippGetStatusString(ippSts), throw(STS_ERR_FAILED));

            pBuffer = (unsigned char*)m_pResize->m_memPool.malloc(iBufferSize);
            if(!pBuffer)
            {
                PRINT_MESSAGE("Cannot allocate memory for Resize buffer");
                throw(STS_ERR_ALLOC);
            }

            status = m_pResize->ResizeBlock(m_pSrcData, m_pDstData, roi, border, pBuffer);
            if(status != STS_OK)
                throw(status);

            m_pResize->m_memPool.free(pBuffer);
        }

        ResizeTBB *m_pResize;
        Image     *m_pSrcData;
        Image     *m_pDstData;
    };

public:
    unsigned int m_iGrainX;
    unsigned int m_iGrainY;

private:
    ResizeTBBTask       m_task;
    auto_partitioner    m_part_auto;
    memory_pool< scalable_allocator<unsigned char> > m_memPool;
};
#endif

int main(int argc, char *argv[])
{
    /*
    // Variables initialization
    */
    Status       status         = STS_OK;
    DString      sInputFile     = CheckTestDirs( BMP_GRAYSCALE_FILE );
    DString      sOutputFile;
    char*        sIppCpu        = 0;
    unsigned int iDstSize[2]    = {0, 0};
#if defined(USE_TBB)
    unsigned int iThreads       = 0;
#else
    unsigned int iThreads       = 1;
#endif
    unsigned int iInter         = 1;
    bool         bNoAspect      = false;
    bool         bNoWindow      = false;
    bool         bPrintHelp     = false;

    // Cubic specific values
    float  fBVal   = 1;
    float  fCVal   = 0;

    // Lanczos specific values
    unsigned int  iLobes  = 3;

    Image srcData;
    Image dstData;
    IppiInterpolationType interpolation;
    Resize *pResize = 0;

    // General timing
    vm_tick      tickStart   = 0;
    vm_tick      tickAcc     = 0;
    vm_tick      tickFreq    = vm_time_get_frequency()/1000;
    double       fTime       = 0;
    unsigned int iTimeLimit  = 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" },
        { 'r', "", 2, cmd::KT_POSITIVE,  0,                 &iDstSize[0],     "destination resolution (width height)" },
        { 'k', "", 1, cmd::KT_BOOL,      0,                 &bNoAspect,       "do not keep aspect ratio" },
#if defined(USE_TBB)
        { 't', "", 1, cmd::KT_INTEGER,   0,                 &iThreads,        "number of threads (0 - auto, 0 by default)" },
#endif
        { 'p', "", 1, cmd::KT_INTEGER,   0,                 &iInter,          "interpolation:\n 0 - Nearest\n 1 - Linear (default)\n 2 - Cubic\n 3 - Lanczos" },
#if defined(ENABLE_RENDERING)
        { 's', "", 1, cmd::KT_BOOL,      0,                 &bNoWindow,       "suppress window output" },
#endif
        { 'w', "", 1, cmd::KT_POSITIVE,  0,                 &iTimeLimit,      "minimum test time in milliseconds" },
        { 'l', "", 1, cmd::KT_POSITIVE,  0,                 &iLoopsLimit,     "number of loops (overrides test time)" },
        { 'T', "", 1, cmd::KT_STRING,    0,                 &sIppCpu,         "target Intel(R) 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);

    printVersion();

    // 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;
    }

    if(iInter == CMD_INTER_NN)
        interpolation = ippNearest;
    else if(iInter == CMD_INTER_LIN)
        interpolation = ippLinear;
    else if(iInter == CMD_INTER_CUB)
        interpolation = ippCubic;
    else if(iInter == CMD_INTER_LAN)
    {
        interpolation = ippLanczos;
        if(iLobes != 2 && iLobes != 3)
        {
            printHelp(cmdOpts, argv);
            PRINT_MESSAGE("Invalid number of lobes");
            return 1;
        }
    }
    else
    {
        printHelp(cmdOpts, argv);
        PRINT_MESSAGE("Invalid interpolation type");
        return 1;
    }

    for(;;)
    {
        // Read from file
        printf("\nInput file: %s\n", sInputFile.c_str());
        status = srcData.Read(sInputFile.c_str());
        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]);

        // Prepare destination buffer
        if(!iDstSize[0])
            iDstSize[0] = (unsigned int)(srcData.m_size.width/2);
        if(!iDstSize[1])
            iDstSize[1] = (unsigned int)(srcData.m_size.height/2);

        if(!iDstSize[0] || !iDstSize[1])
        {
            PRINT_MESSAGE("Invalid output resolution");
            return 1;
        }

        dstData           = srcData;
        dstData.m_size.width  = iDstSize[0];
        dstData.m_size.height = iDstSize[1];

        if(!bNoAspect)
        {
            double fXRatio  = (double)dstData.m_size.width/srcData.m_size.width;
            dstData.m_size.height = (unsigned int)ROUND_NEAR(srcData.m_size.height*fXRatio);
            if(!dstData.m_size.height)
                dstData.m_size.height = 1;
        }

        status = dstData.Alloc();
        CHECK_STATUS_PRINT_BR(status, "Image::Alloc()", GetBaseStatusString(status));

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

        if(iThreads == 1)
        {
            printf("\nSequential resize\n");
            pResize = new Resize;
            if(!pResize)
            {
                PRINT_MESSAGE("Failed to allocate sequential resize class");
                return 1;
            }
        }
#ifdef USE_TBB
        else
        {
            printf("\nTBB resize\n");
            pResize = new ResizeTBB;
            if(!pResize)
            {
                PRINT_MESSAGE("Failed to allocate Intel TBB resize class");
                return 1;
            }
            pResize->m_iThreads = iThreads;
        }
#endif

        // pre-init
        pResize->m_interpolation = interpolation;
        pResize->m_fBVal         = fBVal;
        pResize->m_fCVal         = fCVal;
        pResize->m_iLobes        = iLobes;

        status = pResize->Init(&srcData, &dstData);
        CHECK_STATUS_PRINT_BR(status, "Resize::Init()", GetBaseStatusString(status));

        if(iThreads != 1)
        {
            iThreads = pResize->m_iThreads;
            printf("Threads: %d\n", iThreads);
        }

        printf("\nInterpolation: ");
        if(iInter == CMD_INTER_NN)
            printf("nearest\n");
        else if(iInter == CMD_INTER_LIN)
            printf("linear\n");
        else if(iInter == CMD_INTER_CUB)
            printf("cubic (B = %.2f; C = %.2f)\n", fBVal, fCVal);
        else if(iInter == CMD_INTER_LAN)
            printf("Lanczos (L = %d)\n", iLobes);

        for(iLoops = 1, tickAcc = 0;; iLoops++)
        {
            tickStart = vm_time_get_tick();
            status = pResize->ResizeImage(&srcData, &dstData);
            tickAcc += (vm_time_get_tick() - tickStart);

            CHECK_STATUS_PRINT_BR(status, "Resize::ResizeImage()", GetBaseStatusString(status));

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

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

        if(sOutputFile.Size())
        {
            status = dstData.Write(sOutputFile.c_str());
            CHECK_STATUS_PRINT_BR(status, "Image::Write()", GetBaseStatusString(status));
        }

        // Rendering
        if(!bNoWindow)
        {
            WindowDraw draw("Intel(R) IPP Resize example", WF_FIT_TO_IMAGE);
            if(draw.IsInitialized())
            {
                printf("\nPress Space to cycle through stages:\n");
                printf("1 - result image\n");
                printf("2 - original image\n");
                printf("\nClose window to exit.\n");

                int  iIndex  = 0;
                bool bRedraw = true;
                while(!draw.IsClosed())
                {
                    vm_time_sleep(10);
                    if(draw.CheckKey() == KK_SPACE)
                    {
                        iIndex = (iIndex+1)%2;
                        bRedraw = true;
                    }
                    if(draw.IsInvalidated())
                        bRedraw = true;

                    if(bRedraw)
                    {
                        if(iIndex == 0)
                            draw.DrawImage(&dstData);
                        else if(iIndex == 1)
                            draw.DrawImage(&srcData);
                        bRedraw = false;
                    }
                }
            }
        }

        break;
    }

    if(pResize) delete pResize;
    if(status < 0)
        return status;
    return 0;
}
