/*
* Simd Library (http://ermig1979.github.io/Simd).
*
* Copyright (c) 2011-2019 Yermalayeu Ihar.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __SimdResizer_h__
#define __SimdResizer_h__

#include "Simd/SimdArray.h"
#include "Simd/SimdMath.h"

namespace Simd
{
    struct ResParam
    {
        SimdResizeChannelType type;
        SimdResizeMethodType method;
        size_t srcW, srcH, dstW, dstH, channels, align;

        ResParam(size_t srcW, size_t srcH, size_t dstW, size_t dstH, size_t channels, SimdResizeChannelType type, SimdResizeMethodType method, size_t align)
        {
            this->type = type;
            this->method = method;
            this->srcW = srcW;
            this->srcH = srcH;
            this->dstW = dstW;
            this->dstH = dstH;
            this->channels = channels;
            this->align = align;
        }
    };

    class Resizer : Deletable
    {
    public:
        Resizer(const ResParam & param)
            : _param(param)
        {
        }

        virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride) = 0;

    protected:
        ResParam _param;
    };

    namespace Base
    {
        class ResizerByteBilinear : public Resizer
        {
        protected:
            Array32i _ax, _ix, _ay, _iy, _bx[2];

            void EstimateIndexAlpha(size_t srcSize, size_t dstSize, size_t channels, int32_t * indices, int32_t * alphas);
        public:
            ResizerByteBilinear(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        const int32_t AREA_SHIFT = 22;
        const int32_t AREA_RANGE = 1 << 11;
        const int32_t AREA_ROUND = 1 << 21;

        class ResizerByteArea : public Resizer
        {
        protected:
            Array32i _ax, _ix, _ay, _iy;

            template<size_t N> void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        public:
            ResizerByteArea(const ResParam & param);

            void EstimateParams(size_t srcSize, size_t dstSize, size_t range, int32_t * alpha, int32_t * index);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        class ResizerFloatBilinear : public Resizer
        {
        protected:
            Array32i _ix, _iy;
            Array32f _ax, _ay, _bx[2];

            void EstimateIndexAlpha(size_t srcSize, size_t dstSize, size_t channels, bool caffeInterp, int32_t * indices, float * alphas);

            virtual void Run(const float * src, size_t srcStride, float * dst, size_t dstStride);

        public:
            ResizerFloatBilinear(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        void * ResizerInit(size_t srcX, size_t srcY, size_t dstX, size_t dstY, size_t channels, SimdResizeChannelType type, SimdResizeMethodType method);
    }

#ifdef SIMD_SSE_ENABLE    
    namespace Sse
    {
        class ResizerFloatBilinear : public Base::ResizerFloatBilinear
        {
            virtual void Run(const float * src, size_t srcStride, float * dst, size_t dstStride);
        public:
            ResizerFloatBilinear(const ResParam & param);
        };

        void * ResizerInit(size_t srcX, size_t srcY, size_t dstX, size_t dstY, size_t channels, SimdResizeChannelType type, SimdResizeMethodType method);
    }
#endif //SIMD_SSE_ENABLE 

#ifdef SIMD_SSE2_ENABLE    
    namespace Sse2
    {
        class ResizerByteBilinear : public Base::ResizerByteBilinear
        {
        protected:
            Array16i _ax;
            Array8u _bx[2];

            void EstimateParams();
            template<size_t N> void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        public:
            ResizerByteBilinear(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        class ResizerByteArea : public Base::ResizerByteArea
        {
        protected:
            Array32i _by;

            template<size_t N> void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        public:
            ResizerByteArea(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        void * ResizerInit(size_t srcX, size_t srcY, size_t dstX, size_t dstY, size_t channels, SimdResizeChannelType type, SimdResizeMethodType method);
    }
#endif //SIMD_SSE2_ENABLE 

#ifdef SIMD_SSSE3_ENABLE    
    namespace Ssse3
    {
        class ResizerByteBilinear : public Sse2::ResizerByteBilinear
        {
        protected:
            Array8u _ax;
            size_t _blocks;
            struct Idx
            {
                int32_t src, dst;
                uint8_t shuffle[A];
            };
            Array<Idx> _ixg;

            size_t BlockCountMax(size_t align);
            void EstimateParams();
            template<size_t N> void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
            void RunG(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        public:
            ResizerByteBilinear(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        void * ResizerInit(size_t srcX, size_t srcY, size_t dstX, size_t dstY, size_t channels, SimdResizeChannelType type, SimdResizeMethodType method);
    }
#endif //SIMD_SSSE3_ENABLE 

#ifdef SIMD_SSE41_ENABLE    
    namespace Sse41
    {
        class ResizerByteArea : public Sse2::ResizerByteArea
        {
        protected:
            template<size_t N> void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        public:
            ResizerByteArea(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        void * ResizerInit(size_t srcX, size_t srcY, size_t dstX, size_t dstY, size_t channels, SimdResizeChannelType type, SimdResizeMethodType method);
    }
#endif //SIMD_SSE41_ENABLE

#ifdef SIMD_AVX_ENABLE    
    namespace Avx
    {
        class ResizerFloatBilinear : public Base::ResizerFloatBilinear
        {
            virtual void Run(const float * src, size_t srcStride, float * dst, size_t dstStride);
        public:
            ResizerFloatBilinear(const ResParam & param);
        };

        void * ResizerInit(size_t srcX, size_t srcY, size_t dstX, size_t dstY, size_t channels, SimdResizeChannelType type, SimdResizeMethodType method);
    }
#endif //SIMD_AVX_ENABLE 

#ifdef SIMD_AVX2_ENABLE    
    namespace Avx2
    {
        template <class Idx> SIMD_INLINE void ResizerByteBilinearLoadGrayInterpolated(const uint8_t * src, const Idx & index, const uint8_t * alpha, uint8_t * dst)
        {
            __m256i _src = _mm256_loadu_si256((__m256i*)(src + index.src));
            __m256i _shuffle = _mm256_loadu_si256((__m256i*)&index.shuffle);
            __m256i _alpha = _mm256_loadu_si256((__m256i*)(alpha + index.dst));
            _mm256_storeu_si256((__m256i*)(dst + index.dst), _mm256_maddubs_epi16(Avx2::Shuffle(_src, _shuffle), _alpha));
        }

        class ResizerByteBilinear : public Ssse3::ResizerByteBilinear
        {
        protected:
            struct Idx
            {
                int32_t src, dst;
                uint8_t shuffle[A];
            };
            Array<Idx> _ixg;

            void EstimateParams();
            template<size_t N> void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
            void RunG(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        public:
            ResizerByteBilinear(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        class ResizerByteArea : public Sse41::ResizerByteArea
        {
        protected:
            template<size_t N> void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        public:
            ResizerByteArea(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        class ResizerFloatBilinear : public Base::ResizerFloatBilinear
        {
            virtual void Run(const float * src, size_t srcStride, float * dst, size_t dstStride);
        public:
            ResizerFloatBilinear(const ResParam & param);
        };

        void * ResizerInit(size_t srcX, size_t srcY, size_t dstX, size_t dstY, size_t channels, SimdResizeChannelType type, SimdResizeMethodType method);
    }
#endif //SIMD_AVX2_ENABLE 

#ifdef SIMD_NEON_ENABLE    
    namespace Neon
    {
        class ResizerByteBilinear : public Base::ResizerByteBilinear
        {
        protected:
            Array8u _ax, _bx[2];
            size_t _blocks;
            struct Idx
            {
                int32_t src, dst;
                uint8_t shuffle[A];
            };
            Array<Idx> _ixg;

            size_t BlockCountMax(size_t align);
            void EstimateParams();
            template<size_t N> void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
            void RunG(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        public:
            ResizerByteBilinear(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        class ResizerByteArea : public Base::ResizerByteArea
        {
        protected:
            Array32i _by;

            template<size_t N> void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        public:
            ResizerByteArea(const ResParam & param);

            virtual void Run(const uint8_t * src, size_t srcStride, uint8_t * dst, size_t dstStride);
        };

        class ResizerFloatBilinear : public Base::ResizerFloatBilinear
        {
            virtual void Run(const float * src, size_t srcStride, float * dst, size_t dstStride);
        public:
            ResizerFloatBilinear(const ResParam & param);
        };

        void * ResizerInit(size_t srcX, size_t srcY, size_t dstX, size_t dstY, size_t channels, SimdResizeChannelType type, SimdResizeMethodType method);
    }
#endif //SIMD_NEON_ENABLE 
}
#endif//__SimdResizer_h__
