/*******************************************************************************
* Copyright 2015-2018 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.
*******************************************************************************/

// A simple example of downsampling of the image with
// 5x5 Gaussian kernel using Intel(R) Integrated Primitives (Intel(R) IPP) functions:
//     ippiPyramidGetSize
//     ippiPyramidInit
//     ippiPyramidLayerDownGetSize_32f_C1R
//     ippiPyramidLayerDownInit_32f_C1R
//     ippiPyramidLayerDown_32f_C1R


#include <stdio.h>
#include "ipp.h"

#define WIDTH  128  /* Image width  */
#define HEIGHT 128  /* Image height */
#define KERSIZE 3   /* Kernel size  */

/* Next two defines are created to simplify code reading and understanding */
#define EXIT_MAIN exitLine:                                  /* Label for Exit */
#define check_sts(st) if((st) != ippStsNoErr) goto exitLine; /* Go to Exit if Intel(R) IPP function returned status different from ippStsNoErr */

/* Results of ippMalloc() are not validated because Intel(R) IPP functions perform bad arguments check and will return an appropriate status  */

int main(void)
{
    IppStatus   status = ippStsNoErr;
    Ipp32f*     pSrc   = NULL;  /* Pointer to source image */
    int         srcStep;        /* Step, in bytes, through the source image */

    IppiSize    roiSize = { WIDTH, HEIGHT };    /* Size of source/destination ROI in pixels */
    Ipp32f      rate    = 2.f;                  /* Neighbor levels ratio */
    int         level   = 1000;                 /* Maximal number pyramid level */

    int i = 0;
    int pyrBufferSize = 0;
    int pyrStructSize = 0;

    IppiPyramid *pPyrStruct    = NULL;
    Ipp8u       *pPyrBuffer    = NULL;
    Ipp8u       *pPyrStrBuffer = NULL;

    int      pyrLStateSize  = 0;
    int      pyrLBufferSize = 0;
    Ipp8u   *pPyrLStateBuf  = NULL;
    Ipp8u   *pPyrLBuffer    = NULL;
    Ipp32f **pPyrImage      = NULL;

    Ipp32f kernel[KERSIZE] = { 1.f, 1.f, 1.f }; /* Separable symmetric kernel of odd length */

    pSrc = ippiMalloc_32f_C1(roiSize.width, roiSize.height, &srcStep);

    /* Build Gaussian pyramid */
    {
        /* Computes the temporary work buffer size */
        check_sts( status = ippiPyramidGetSize(&pyrStructSize, &pyrBufferSize, level, roiSize, rate) )

        pPyrBuffer    = ippsMalloc_8u(pyrBufferSize);
        pPyrStrBuffer = ippsMalloc_8u(pyrStructSize);

        /* Initializes Gaussian structure for pyramids */
        check_sts( status = ippiPyramidInit(&pPyrStruct, level, roiSize, rate, pPyrStrBuffer, pPyrBuffer) )

        /* Correct maximum scale level */
        level = pPyrStruct->level;

        /* Allocate structures to calculate pyramid layers */
        check_sts( status = ippiPyramidLayerDownGetSize_32f_C1R(roiSize, rate, KERSIZE, &pyrLStateSize, &pyrLBufferSize) )

        pPyrLStateBuf = ippsMalloc_8u(pyrLStateSize);
        pPyrLBuffer   = ippsMalloc_8u(pyrLBufferSize);

        /* Initialize the structure for creating a lower pyramid layer */
        check_sts( status = ippiPyramidLayerDownInit_32f_C1R((IppiPyramidDownState_32f_C1R**)&pPyrStruct->pState, roiSize, rate,
            kernel, KERSIZE, IPPI_INTER_LINEAR, pPyrLStateBuf, pPyrLBuffer) )

        /* Allocate pyramid layers */
        pPyrImage    = (Ipp32f**)pPyrStruct->pImage;
        pPyrImage[0] = pSrc;
        pPyrStruct->pStep[0] = srcStep;
        for(i = 1; i <= level; i++)
        {
            pPyrImage[i] = ippiMalloc_32f_C1(pPyrStruct->pRoi[i].width, pPyrStruct->pRoi[i].height, &pPyrStruct->pStep[i]);
        }

        /* Perform downsampling of the image with 5x5 Gaussian kernel */
        for(i = 1; i <= level; i++)
        {
            check_sts( status = ippiPyramidLayerDown_32f_C1R(pPyrImage[i - 1], pPyrStruct->pStep[i - 1], pPyrStruct->pRoi[i - 1],
                pPyrImage[i], pPyrStruct->pStep[i], pPyrStruct->pRoi[i], (IppiPyramidDownState_32f_C1R*)pPyrStruct->pState) )
        }
    }

EXIT_MAIN
    if (pPyrImage)
        for(i = 1; i <= level; i++)
            ippiFree(pPyrImage[i]);
    ippiFree(pPyrLStateBuf);
    ippiFree(pPyrLBuffer);
    ippiFree(pPyrStrBuffer);
    ippiFree(pPyrBuffer);
    ippiFree(pSrc);
    printf("Exit status %d (%s)\n", (int)status, ippGetStatusString(status));
    return status;
}