/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#include "_cv.h"

/****************************************************************************************\
*                                        S O B E L                                       *
\****************************************************************************************/

/* This function calculates generalized Sobel kernel */
static int icvCalcKer( char *kernel, int order, int size,
                       CvDataType datatype, int origin )
{
    int i, j;
    int* kerI = (int*)kernel;
    int type = -1;

    if( size != CV_SCHARR )
    {
        if( size == 3 )
        {
            switch( order )
            {
            case 0:
                kerI[0] = 1;
                kerI[1] = 2;
                kerI[2] = 1;
                type = ICV_1_2_1_KERNEL;
                break;
            case 1:
                kerI[0] =-1;
                kerI[1] = 0;
                kerI[2] = 1;
                type = ICV_m1_0_1_KERNEL;
                break;
            case 2:
                kerI[0] = 1;
                kerI[1] =-2;
                kerI[2] = 1;
                type = ICV_1_m2_1_KERNEL;
                break;
            default:
                return CV_BADARG_ERR;
            }
        }
        else
        {
            int oldval, newval;

            memset( kerI + 1, 0, size * sizeof(kerI[0]));
            kerI[0] = 1;

            for( i = 0; i < size - order - 1; i++ )
            {
                oldval = kerI[0];
                for( j = 1; j <= size; j++ )
                {
                    newval = kerI[j]+kerI[j-1];
                    kerI[j-1] = oldval;
                    oldval = newval;
                }
            }

            for( i = 0; i < order; i++ )
            {
                oldval = -kerI[0];
                for( j = 1; j <= size; j++ )
                {
                    newval = kerI[j-1] - kerI[j];
                    kerI[j-1] = oldval;
                    oldval = newval;
                }
            }

            type = order & 1 ? ICV_ASYMMETRIC_KERNEL : ICV_SYMMETRIC_KERNEL;
        }
    }
    else
    {
        size = 3;
        if( order == 1 )
        {
            kerI[0] = -1;
            kerI[1] = 0;
            kerI[2] = 1;
            type = ICV_m1_0_1_KERNEL;
        }
        else
        {
            assert( order == 0 );
            kerI[0] = kerI[2] = 3;
            kerI[1] = 10;
            type = ICV_3_10_3_KERNEL;
        }
    }

    if( origin && (order & 1) )
        for( j = 0; j < size; j++ )
            kerI[j] = -kerI[j];

    if( datatype == cv32f )
        for( j = 0; j < size; j++ )
            ((float*)kerI)[j] = (float)kerI[j];

    return type;
}


CvFilterState* icvSobelInitAlloc( int roiwidth, int datatype, int size,
                                  int origin, int dx, int dy )
{
    CvFilterState* state = 0;

    #define MAX_KERNEL_SIZE  7
    int ker[MAX_KERNEL_SIZE*2+1];
    CvDataType worktype = datatype != cv32f ? cv32s : cv32f;
    int x_filter_type, y_filter_type;
    int x_size = size, y_size = size;

    CV_FUNCNAME( "icvSobelInitAlloc" );

    __BEGIN__;

    if( size == CV_SCHARR )
    {
        if( dx + dy != 1 )
            CV_ERROR( CV_StsOutOfRange, "Sharr filter can be used only for the first derivatives" );
        x_size = y_size = 3;
    }
    else
    {
        if( (size&1) == 0 || size < 1 || size > MAX_KERNEL_SIZE )
            CV_ERROR( CV_StsBadSize, "Sobel filter size can be 1, 3, 5 or 7" );

        if( (unsigned)dx > 2 || (unsigned)dy > 2 )
            CV_ERROR( CV_StsOutOfRange, "Derivatives of order>2 can not be computed now" );

        if( size == 1 )
        {
            if( dy == 0 )
                x_size = 3, y_size = 1;
            else if( dx == 0 )
                x_size = 1, y_size = 3;
            else
                CV_ERROR( CV_StsOutOfRange, "1x3 or 3x1 filter can not be used for mixed derivatives" );
        }
    }

    x_filter_type = icvCalcKer( (char*)ker, dx, size < 0 ? size : x_size, worktype, 0 );
    y_filter_type = icvCalcKer( (char*)(ker + x_size), dy, size < 0 ? size : y_size,
                                worktype, origin != 0 );

    {
    CvSize element_size = { x_size, y_size };
    CvPoint element_anchor = { x_size/2, y_size/2 };

    state = icvFilterInitAlloc( roiwidth, worktype, 1, element_size, element_anchor, ker,
                                ICV_MAKE_SEPARABLE_KERNEL(x_filter_type, y_filter_type) );
    }

    if( state )
        state->origin = origin != 0;

    __END__;

    return state;
}


CvStatus CV_STDCALL icvSobel_8u16s_C1R( const uchar* pSrc, int srcStep,
                                        short* dst, int dstStep, CvSize* roiSize,
                                        CvFilterState* state, int stage )
{
    uchar* src = (uchar*)pSrc;
    int width = roiSize->width;
    int src_height = roiSize->height;
    int dst_height = src_height;
    int x, y = 0, i;

    int ker_width = state->ker_width;
    int ker_height = state->ker_height;
    int ker_x = ker_width/2;
    int ker_y = ker_height/2;
    int ker_right = ker_width - ker_x;

    int crows = state->crows;
    int **rows = (int**)(state->rows);
    short *tbufw = (short*)(state->tbuf);
    int *trow = 0;

    int* fmaskX = (int*)(state->ker0) + ker_x;
    int* fmaskY = (int*)(state->ker1) + ker_y;
    int fmX0 = fmaskX[0], fmY0 = fmaskY[0];

    int is_small_width = width <= ker_width;
    int starting_flag = 0;
    int width_rest = width & (CV_MORPH_ALIGN - 1);
    int origin = state->origin;
    int x_type = ICV_X_KERNEL_TYPE(state->kerType),
        y_type = ICV_Y_KERNEL_TYPE(state->kerType);
    int x_asymm = (x_type & 3) - 1, /* <0 - general kind (not used),
                                       0-symmetric, 1-asymmetric*/
        y_asymm = (y_type & 3) - 1;

    if( stage == CV_START + CV_END )
        stage = CV_WHOLE;

    /* initialize cyclic buffer when starting */
    if( stage == CV_WHOLE || stage == CV_START )
    {
        for( i = 0; i < ker_height; i++ )
            rows[i] = (int*)(state->buffer + state->buffer_step * i);

        crows = ker_y;
        if( stage != CV_WHOLE )
            dst_height -= ker_height - ker_y - 1;
        starting_flag = 1;
    }

    if( stage == CV_END )
        dst_height += ker_height - ker_y - 1;

    dstStep /= sizeof(dst[0]);

    do
    {
        int need_copy = is_small_width | (y == 0);
        uchar *tsrc;
        int   *tdst;
        short *tdst2;
        int   *saved_row = rows[ker_y];

        /* fill cyclic buffer - horizontal filtering */
        for( ; crows < ker_height; crows++ )
        {
            tsrc = src - ker_x;
            tdst = rows[crows];

            if( src_height-- <= 0 )
            {
                if( stage != CV_WHOLE && stage != CV_END )
                    break;
                /* duplicate last row */
                trow = rows[crows - 1];
                CV_COPY( tdst, trow, width, x );
                continue;
            }

            need_copy |= src_height == 0;

            if( ker_width > 1 )
            {
                uchar* tbufc = (uchar*)tbufw;

                if( need_copy )
                {
                    tsrc = tbufc - ker_x;
                    CV_COPY( tbufc, src, width, x );
                }
                else
                {
                    CV_COPY( tbufc - ker_x, src - ker_x, ker_x, x );
                    CV_COPY( tbufc, src + width, ker_right, x );
                }

                /* make replication borders */
                {
                uchar pix = tsrc[ker_x];
                CV_SET( tsrc, pix, ker_x, x );

                pix = tsrc[width + ker_x - 1];
                CV_SET( tsrc + width + ker_x, pix, ker_right, x );
                }

                if( x_asymm )
                {
                    /* horizontal filter: asymmetric case */
                    if( x_type == ICV_m1_0_1_KERNEL )
                    {
                        for( i = 0; i < width; i++ )
                            tdst[i] = tsrc[i+2] - tsrc[i];
                    }
                    else
                    {
                        for( i = 0; i < width; i++ )
                        {
                            int j;
                            int t0 = tsrc[i + ker_x]*fmX0;

                            for( j = 1; j <= ker_x; j++ )
                                t0 += (tsrc[i+ker_x+j] - tsrc[i+ker_x-j])*fmaskX[j];

                            tdst[i] = t0;
                        }
                    }
                }
                else
                {
                    if( x_type == ICV_1_2_1_KERNEL )
                    {
                        for( i = 0; i < width; i++ )
                            tdst[i] = tsrc[i+1]*2 + tsrc[i] + tsrc[i+2];
                    }
                    else if( x_type == ICV_3_10_3_KERNEL )
                    {
                        for( i = 0; i < width; i++ )
                            tdst[i] = tsrc[i+1]*10 + (tsrc[i] + tsrc[i+2])*3;
                    }
                    else
                    {
                        /* horizontal filter: symmetric case */
                        for( i = 0; i < width; i++ )
                        {
                            int j;
                            int t0 = tsrc[i + ker_x]*fmX0;

                            for( j = 1; j <= ker_x; j++ )
                                t0 += (tsrc[i+ker_x+j] + tsrc[i+ker_x-j])*fmaskX[j];

                            tdst[i] = t0;
                        }
                    }
                }

                if( !need_copy )
                {
                    /* restore borders */
                    CV_COPY( src - ker_x, tbufc - ker_x, ker_x, x );
                    CV_COPY( src + width, tbufc, ker_right, x );
                }
            }
            else
            {
                CV_COPY( tdst, tsrc + ker_x, width, x );
            }

            if( crows < ker_height )
                src += srcStep;
        }

        if( starting_flag )
        {
            starting_flag = 0;
            trow = rows[ker_y];

            for( i = 0; i < ker_y; i++ )
            {
                tdst = rows[i];
                CV_COPY( tdst, trow, width, x );
            }
        }

        /* vertical convolution */
        if( crows != ker_height )
            break;

        tdst2 = dst;
        trow = rows[ker_y];

        if( ker_height > 1 )
        {
            if( width_rest )
            {
                need_copy = width < CV_MORPH_ALIGN || y == dst_height - 1;

                if( need_copy )
                    tdst2 = tbufw;
                else
                    CV_COPY( tbufw + width, dst + width, CV_MORPH_ALIGN, x );
            }

            if( y_asymm )
            {
                if( y_type == ICV_m1_0_1_KERNEL )
                {
                    int *trow1 = rows[origin*2], *trow2 = rows[(origin^1)*2];

                    for( x = 0; x < width; x += 4 )
                    {
                        int val0, val1;
                        val0 = trow2[x] - trow1[x];
                        val1 = trow2[x + 1] - trow1[x + 1];

                        tdst2[x + 0] = (short)val0;
                        tdst2[x + 1] = (short)val1;

                        val0 = trow2[x + 2] - trow1[x + 2];
                        val1 = trow2[x + 3] - trow1[x + 3];

                        tdst2[x + 2] = (short)val0;
                        tdst2[x + 3] = (short)val1;
                    }
                }
                else
                {
                    for( x = 0; x < width; x += 4 )
                    {
                        int val0, val1, val2, val3;

                        val0 = trow[x]*fmY0;
                        val1 = trow[x + 1]*fmY0;
                        val2 = trow[x + 2]*fmY0;
                        val3 = trow[x + 3]*fmY0;

                        for( i = 1; i <= ker_y; i++ )
                        {
                            int *trow1, *trow2;
                            int m = fmaskY[i];
                            trow1 = rows[ker_y - i];
                            trow2 = rows[ker_y + i];
                            val0 += (trow2[x] - trow1[x])*m;
                            val1 += (trow2[x+1] - trow1[x+1])*m;
                            val2 += (trow2[x+2] - trow1[x+2])*m;
                            val3 += (trow2[x+3] - trow1[x+3])*m;
                        }

                        tdst2[x + 0] = CV_CAST_16S(val0);
                        tdst2[x + 1] = CV_CAST_16S(val1);
                        tdst2[x + 2] = CV_CAST_16S(val2);
                        tdst2[x + 3] = CV_CAST_16S(val3);
                    }
                }
            }
            else
            {
                if( y_type == ICV_1_2_1_KERNEL )
                {
                    int *trow1 = rows[0], *trow2 = rows[2];

                    for( x = 0; x < width; x += 4 )
                    {
                        int val0, val1;
                        val0 = trow[x]*2 + trow1[x] + trow2[x];
                        val1 = trow[x + 1]*2 + trow1[x+1] + trow2[x+1];

                        tdst2[x + 0] = (short)val0;
                        tdst2[x + 1] = (short)val1;

                        val0 = trow[x + 2]*2 + trow1[x+2] + trow2[x+2];
                        val1 = trow[x + 3]*2 + trow1[x+3] + trow2[x+3];

                        tdst2[x + 2] = (short)val0;
                        tdst2[x + 3] = (short)val1;
                    }
                }
                else if( y_type == ICV_3_10_3_KERNEL )
                {
                    int *trow1 = rows[0], *trow2 = rows[2];

                    for( x = 0; x < width; x += 4 )
                    {
                        int val0, val1;
                        val0 = trow[x]*10 + (trow1[x] + trow2[x])*3;
                        val1 = trow[x + 1]*10 + (trow1[x+1] + trow2[x+1])*3;

                        tdst2[x + 0] = (short)val0;
                        tdst2[x + 1] = (short)val1;

                        val0 = trow[x + 2]*10 + (trow1[x+2] + trow2[x+2])*3;
                        val1 = trow[x + 3]*10 + (trow1[x+3] + trow2[x+3])*3;

                        tdst2[x + 2] = (short)val0;
                        tdst2[x + 3] = (short)val1;
                    }
                }
                else
                {
                    for( x = 0; x < width; x += 4 )
                    {
                        int val0, val1, val2, val3;

                        val0 = trow[x]*fmY0;
                        val1 = trow[x + 1]*fmY0;
                        val2 = trow[x + 2]*fmY0;
                        val3 = trow[x + 3]*fmY0;

                        for( i = 1; i <= ker_y; i++ )
                        {
                            int *trow1, *trow2;
                            int m = fmaskY[i];
                            trow1 = rows[ker_y - i];
                            trow2 = rows[ker_y + i];
                            val0 += (trow2[x] + trow1[x])*m;
                            val1 += (trow2[x+1] + trow1[x+1])*m;
                            val2 += (trow2[x+2] + trow1[x+2])*m;
                            val3 += (trow2[x+3] + trow1[x+3])*m;
                        }

                        tdst2[x + 0] = CV_CAST_16S(val0);
                        tdst2[x + 1] = CV_CAST_16S(val1);
                        tdst2[x + 2] = CV_CAST_16S(val2);
                        tdst2[x + 3] = CV_CAST_16S(val3);
                    }
                }
            }

            if( width_rest )
            {
                if( need_copy )
                    CV_COPY( dst, tbufw, width, x );
                else
                    CV_COPY( dst + width, tbufw + width, CV_MORPH_ALIGN, x );
            }
        }
        else
        {
            for( x = 0; x < width; x++ )
                dst[x] = (short)trow[x];
        }

        rows[ker_y] = saved_row;

        /* rotate buffer */
        {
            int *t = rows[0];

            CV_COPY( rows, rows + 1, ker_height - 1, i );
            rows[i] = t;
            crows--;
            dst += dstStep;
        }
    }
    while( ++y < dst_height );

    roiSize->height = y;
    state->crows = crows;

    return CV_OK;
}


CvStatus CV_STDCALL icvSobel_32f_C1R( const float* pSrc, int srcStep,
                                      float* dst, int dstStep, CvSize* roiSize,
                                      CvFilterState* state, int stage )
{
    float* src = (float*)pSrc;
    int width = roiSize->width;
    int src_height = roiSize->height;
    int dst_height = src_height;
    int x, y = 0, i;

    int ker_width = state->ker_width;
    int ker_height = state->ker_height;
    int ker_x = ker_width/2;
    int ker_y = ker_height/2;
    int ker_right = ker_width - ker_x;

    int crows = state->crows;
    float **rows = (float**)(state->rows);
    float *tbufw = (float*)(state->tbuf);
    float *trow = 0;

    float* fmaskX = (float*)(state->ker0) + ker_x;
    float* fmaskY = (float*)(state->ker1) + ker_y;
    float fmX0 = fmaskX[0], fmY0 = fmaskY[0];

    int is_small_width = width <= ker_width;
    int starting_flag = 0;
    int width_rest = width & (CV_MORPH_ALIGN - 1);
    int origin = state->origin;
    int x_type = ICV_X_KERNEL_TYPE(state->kerType),
        y_type = ICV_Y_KERNEL_TYPE(state->kerType);
    int x_asymm = (x_type & 3) - 1, /* <0 - general kind (not used),
                                       0-symmetric, 1-asymmetric*/
        y_asymm = (y_type & 3) - 1;

    if( stage == CV_START + CV_END )
        stage = CV_WHOLE;

    /* initialize cyclic buffer when starting */
    if( stage == CV_WHOLE || stage == CV_START )
    {
        for( i = 0; i < ker_height; i++ )
        {
            rows[i] = (float*)(state->buffer + state->buffer_step * i);
            for( x = width; (x&3) != 0; x++ )
                rows[i][x] = 0.f;
        }

        crows = ker_y;
        if( stage != CV_WHOLE )
            dst_height -= ker_height - ker_y - 1;
        starting_flag = 1;
    }

    if( stage == CV_END )
        dst_height += ker_height - ker_y - 1;

    srcStep /= sizeof(src[0]);
    dstStep /= sizeof(dst[0]);

    do
    {
        int need_copy = is_small_width | (y == 0);
        float *tsrc;
        float *tdst;
        float *tdst2;
        float *saved_row = rows[ker_y];

        /* fill cyclic buffer - horizontal filtering */
        for( ; crows < ker_height; crows++ )
        {
            tsrc = src - ker_x;
            tdst = rows[crows];

            if( src_height-- <= 0 )
            {
                if( stage != CV_WHOLE && stage != CV_END )
                    break;
                /* duplicate last row */
                trow = rows[crows - 1];
                CV_COPY( tdst, trow, width, x );
                continue;
            }

            need_copy |= src_height == 0;

            if( ker_width > 1 )
            {
                float* tbufc = (float*)tbufw;

                if( need_copy )
                {
                    tsrc = tbufc - ker_x;
                    CV_COPY( tbufc, src, width, x );
                }
                else
                {
                    CV_COPY( tbufc - ker_x, src - ker_x, ker_x, x );
                    CV_COPY( tbufc, src + width, ker_right, x );
                }

                /* make replication borders */
                {
                float pix = tsrc[ker_x];
                CV_SET( tsrc, pix, ker_x, x );

                pix = tsrc[width + ker_x - 1];
                CV_SET( tsrc + width + ker_x, pix, ker_right, x );
                }

                if( x_asymm )
                {
                    /* horizontal filter: asymmetric case */
                    if( x_type == ICV_m1_0_1_KERNEL )
                    {
                        for( i = 0; i < width; i++ )
                            tdst[i] = tsrc[i+2] - tsrc[i];
                    }
                    else
                    {
                        for( i = 0; i < width; i++ )
                        {
                            int j;
                            float t0 = tsrc[i + ker_x]*fmX0;

                            for( j = 1; j <= ker_x; j++ )
                                t0 += (tsrc[i+ker_x+j] - tsrc[i+ker_x-j])*fmaskX[j];

                            tdst[i] = t0;
                        }
                    }
                }
                else
                {
                    if( x_type == ICV_1_2_1_KERNEL )
                    {
                        for( i = 0; i < width; i++ )
                            tdst[i] = tsrc[i+1]*2 + tsrc[i] + tsrc[i+2];
                    }
                    else if( x_type == ICV_3_10_3_KERNEL )
                    {
                        for( i = 0; i < width; i++ )
                            tdst[i] = tsrc[i+1]*10 + (tsrc[i] + tsrc[i+2])*3;
                    }
                    else
                    {
                        /* horizontal filter: symmetric case */
                        for( i = 0; i < width; i++ )
                        {
                            int j;
                            double t0 = tsrc[i + ker_x]*fmX0;

                            for( j = 1; j <= ker_x; j++ )
                                t0 += (double)(tsrc[i+ker_x+j] + tsrc[i+ker_x-j])*fmaskX[j];

                            tdst[i] = (float)t0;
                        }
                    }
                }

                if( !need_copy )
                {
                    /* restore borders */
                    CV_COPY( src - ker_x, tbufc - ker_x, ker_x, x );
                    CV_COPY( src + width, tbufc, ker_right, x );
                }
            }
            else
            {
                CV_COPY( tdst, tsrc + ker_x, width, x );
            }

            if( crows < ker_height )
                src += srcStep;
        }

        if( starting_flag )
        {
            starting_flag = 0;
            trow = rows[ker_y];

            for( i = 0; i < ker_y; i++ )
            {
                tdst = rows[i];
                CV_COPY( tdst, trow, width, x );
            }
        }

        /* vertical convolution */
        if( crows != ker_height )
            break;

        tdst2 = dst;
        trow = rows[ker_y];

        if( ker_height > 1 )
        {
            if( width_rest )
            {
                need_copy = width < CV_MORPH_ALIGN || y == dst_height - 1;

                if( need_copy )
                    tdst2 = tbufw;
                else
                    CV_COPY( tbufw + width, dst + width, CV_MORPH_ALIGN, x );
            }

            if( y_asymm )
            {
                if( y_type == ICV_m1_0_1_KERNEL )
                {
                    float *trow1 = rows[origin*2], *trow2 = rows[(origin^1)*2];

                    for( x = 0; x < width; x += 4 )
                    {
                        double val0, val1;
                        val0 = trow2[x] - trow1[x];
                        val1 = trow2[x + 1] - trow1[x + 1];

                        tdst2[x + 0] = (float)val0;
                        tdst2[x + 1] = (float)val1;

                        val0 = trow2[x + 2] - trow1[x + 2];
                        val1 = trow2[x + 3] - trow1[x + 3];

                        tdst2[x + 2] = (float)val0;
                        tdst2[x + 3] = (float)val1;
                    }
                }
                else
                {
                    for( x = 0; x < width; x += 4 )
                    {
                        double val0, val1, val2, val3;

                        val0 = trow[x]*fmY0;
                        val1 = trow[x + 1]*fmY0;
                        val2 = trow[x + 2]*fmY0;
                        val3 = trow[x + 3]*fmY0;

                        for( i = 1; i <= ker_y; i++ )
                        {
                            float *trow1, *trow2;
                            double m = fmaskY[i];
                            trow1 = rows[ker_y - i];
                            trow2 = rows[ker_y + i];
                            val0 += (trow2[x] - trow1[x])*m;
                            val1 += (trow2[x+1] - trow1[x+1])*m;
                            val2 += (trow2[x+2] - trow1[x+2])*m;
                            val3 += (trow2[x+3] - trow1[x+3])*m;
                        }

                        tdst2[x + 0] = (float)val0;
                        tdst2[x + 1] = (float)val1;
                        tdst2[x + 2] = (float)val2;
                        tdst2[x + 3] = (float)val3;
                    }
                }
            }
            else
            {
                if( y_type == ICV_1_2_1_KERNEL )
                {
                    float *trow1 = rows[0], *trow2 = rows[2];

                    for( x = 0; x < width; x += 4 )
                    {
                        double val0, val1;
                        val0 = trow[x]*2 + trow1[x] + trow2[x];
                        val1 = trow[x + 1]*2 + trow1[x+1] + trow2[x+1];

                        tdst2[x + 0] = (float)val0;
                        tdst2[x + 1] = (float)val1;

                        val0 = trow[x + 2]*2 + trow1[x+2] + trow2[x+2];
                        val1 = trow[x + 3]*2 + trow1[x+3] + trow2[x+3];

                        tdst2[x + 2] = (float)val0;
                        tdst2[x + 3] = (float)val1;
                    }
                }
                else if( y_type == ICV_3_10_3_KERNEL )
                {
                    float *trow1 = rows[0], *trow2 = rows[2];

                    for( x = 0; x < width; x += 4 )
                    {
                        double val0, val1;
                        val0 = trow[x]*10 + (trow1[x] + trow2[x])*3;
                        val1 = trow[x + 1]*10 + (trow1[x+1] + trow2[x+1])*3;

                        tdst2[x + 0] = (float)val0;
                        tdst2[x + 1] = (float)val1;

                        val0 = trow[x + 2]*10 + (trow1[x+2] + trow2[x+2])*3;
                        val1 = trow[x + 3]*10 + (trow1[x+3] + trow2[x+3])*3;

                        tdst2[x + 2] = (float)val0;
                        tdst2[x + 3] = (float)val1;
                    }
                }
                else
                {
                    for( x = 0; x < width; x += 4 )
                    {
                        double val0, val1, val2, val3;

                        val0 = trow[x]*fmY0;
                        val1 = trow[x + 1]*fmY0;
                        val2 = trow[x + 2]*fmY0;
                        val3 = trow[x + 3]*fmY0;

                        for( i = 1; i <= ker_y; i++ )
                        {
                            float *trow1, *trow2;
                            double m = fmaskY[i];
                            trow1 = rows[ker_y - i];
                            trow2 = rows[ker_y + i];
                            val0 += (trow2[x] + trow1[x])*m;
                            val1 += (trow2[x+1] + trow1[x+1])*m;
                            val2 += (trow2[x+2] + trow1[x+2])*m;
                            val3 += (trow2[x+3] + trow1[x+3])*m;
                        }

                        tdst2[x + 0] = (float)val0;
                        tdst2[x + 1] = (float)val1;
                        tdst2[x + 2] = (float)val2;
                        tdst2[x + 3] = (float)val3;
                    }
                }
            }

            if( width_rest )
            {
                if( need_copy )
                    CV_COPY( dst, tbufw, width, x );
                else
                    CV_COPY( dst + width, tbufw + width, CV_MORPH_ALIGN, x );
            }
        }
        else
        {
            for( x = 0; x < width; x++ )
                dst[x] = (float)trow[x];
        }

        rows[ker_y] = saved_row;

        /* rotate buffer */
        {
            float *t = rows[0];

            CV_COPY( rows, rows + 1, ker_height - 1, i );
            rows[i] = t;
            crows--;
            dst += dstStep;
        }
    }
    while( ++y < dst_height );

    roiSize->height = y;
    state->crows = crows;

    return CV_OK;
}


/****************************************************************************************\
*                                      S C H A R R                                       *
\****************************************************************************************/

static CvFilterState* CV_STDCALL
icvScharrInitAlloc( int roiwidth, int datatype, int origin, int dx, int dy )
{
    return icvSobelInitAlloc( roiwidth, datatype, CV_SCHARR, origin, dx, dy );
}

static CvStatus CV_STDCALL
icvScharr_8u16s_C1R( const uchar* pSrc, int srcStep,
                     short* dst, int dstStep, CvSize* roiSize,
                     CvFilterState* state, int stage )
{
    assert( state->kerType == ICV_MAKE_SEPARABLE_KERNEL( ICV_m1_0_1_KERNEL, ICV_3_10_3_KERNEL ) ||
            state->kerType == ICV_MAKE_SEPARABLE_KERNEL( ICV_3_10_3_KERNEL, ICV_m1_0_1_KERNEL ));
    return icvSobel_8u16s_C1R( pSrc, srcStep, dst, dstStep, roiSize, state, stage );
}


static CvStatus CV_STDCALL
icvScharr_32f_C1R( const float* pSrc, int srcStep,
                   float* dst, int dstStep, CvSize* roiSize,
                   CvFilterState* state, int stage )
{
    assert( state->kerType == ICV_MAKE_SEPARABLE_KERNEL( ICV_m1_0_1_KERNEL, ICV_3_10_3_KERNEL ) ||
            state->kerType == ICV_MAKE_SEPARABLE_KERNEL( ICV_3_10_3_KERNEL, ICV_m1_0_1_KERNEL ));
    return icvSobel_32f_C1R( pSrc, srcStep, dst, dstStep, roiSize, state, stage );
}

/****************************************************************************************\
*                                      L A P L A C E                                     *
\****************************************************************************************/

static CvFilterState* CV_STDCALL
icvLaplaceInitAlloc( int roiwidth, int datatype, int size )
{
    CvFilterState* state = 0;

    #define MAX_KERNEL_SIZE  7
    int ker[MAX_KERNEL_SIZE*2+1];
    CvDataType worktype = datatype != cv32f ? cv32s : cv32f;
    int x_filter_type, y_filter_type;
    int x_size = size;

    CV_FUNCNAME( "icvLaplaceInitAlloc" );

    __BEGIN__;

    if( (size&1) == 0 || size < 1 || size > MAX_KERNEL_SIZE )
        CV_ERROR( CV_StsBadSize, "Aperture size can be 1, 3, 5 or 7" );

    if( size == 1 )
        x_size = 3;

    x_filter_type = icvCalcKer( (char*)ker, 2, x_size, worktype, 0 );
    y_filter_type = icvCalcKer( (char*)(ker + x_size), 0, size, worktype, 0 );

    {
    CvSize element_size = { x_size, x_size };
    CvPoint element_anchor = { x_size/2, x_size/2 };

    state = icvFilterInitAlloc( roiwidth, worktype, 2, element_size, element_anchor, ker,
                                ICV_MAKE_SEPARABLE_KERNEL(x_filter_type, y_filter_type));
    }

    if( state )
        state->origin = 0;

    __END__;

    return state;
}


static CvStatus CV_STDCALL
icvLaplace_8u16s_C1R( const uchar* pSrc, int srcStep,
                      short* dst, int dstStep, CvSize* roiSize,
                      CvFilterState* state, int stage )
{
    uchar* src = (uchar*)pSrc;
    int width = roiSize->width;
    int src_height = roiSize->height;
    int dst_height = src_height;
    int x, y = 0, i;

    int ker_width = state->ker_width;
    int ker_height = state->ker_height;
    int ker_x = ker_width/2;
    int ker_y = ker_height/2;
    int ker_right = ker_width - ker_x;

    int crows = state->crows;
    int **rows = (int**)(state->rows);
    short *tbufw = (short*)(state->tbuf);
    int *trow = 0;

    int* fmaskX = (int*)(state->ker0) + ker_x;
    int* fmaskY = (int*)(state->ker1) + ker_y;
    int fmX0 = fmaskX[0], fmY0 = fmaskY[0];

    int is_small_width = width <= ker_width;
    int starting_flag = 0;
    int width_rest = width & (CV_MORPH_ALIGN - 1);
    int y_type = ICV_Y_KERNEL_TYPE(state->kerType);

    if( stage == CV_START + CV_END )
        stage = CV_WHOLE;

    /* initialize cyclic buffer when starting */
    if( stage == CV_WHOLE || stage == CV_START )
    {
        for( i = 0; i < ker_height; i++ )
            rows[i] = (int*)(state->buffer + state->buffer_step * i);

        crows = ker_y;
        if( stage != CV_WHOLE )
            dst_height -= ker_height - ker_y - 1;
        starting_flag = 1;
    }

    if( stage == CV_END )
        dst_height += ker_height - ker_y - 1;

    dstStep /= sizeof(dst[0]);

    do
    {
        int need_copy = is_small_width | (y == 0);
        uchar *tsrc;
        int   *tdst;
        short *tdst2;
        int   *saved_row = rows[ker_y];

        /* fill cyclic buffer - horizontal filtering */
        for( ; crows < ker_height; crows++ )
        {
            tsrc = src - ker_x;
            tdst = rows[crows];

            if( src_height-- <= 0 )
            {
                if( stage != CV_WHOLE && stage != CV_END )
                    break;
                /* duplicate last row */
                trow = rows[crows - 1];
                CV_COPY( tdst, trow, width*2, x );
                continue;
            }

            need_copy |= src_height == 0;

            {
                uchar* tbufc = (uchar*)tbufw;

                if( need_copy )
                {
                    tsrc = tbufc - ker_x;
                    CV_COPY( tbufc, src, width, x );
                }
                else
                {
                    CV_COPY( tbufc - ker_x, src - ker_x, ker_x, x );
                    CV_COPY( tbufc, src + width, ker_right, x );
                }

                /* make replication borders */
                {
                uchar pix = tsrc[ker_x];
                CV_SET( tsrc, pix, ker_x, x );

                pix = tsrc[width + ker_x - 1];
                CV_SET( tsrc + width + ker_x, pix, ker_right, x );
                }

                if( ker_width == 3 )
                {
                    if( y_type == ICV_1_2_1_KERNEL )
                    {
                        for( i = 0; i < width; i++ )
                        {
                            int t0 = tsrc[i] + tsrc[i+2] - tsrc[i+1]*2;
                            int t1 = tsrc[i] + tsrc[i+2] + tsrc[i+1]*2;
                            tdst[i] = t0;
                            tdst[i+width] = t1;
                        }
                    }
                    else
                    {
                        for( i = 0; i < width; i++ )
                        {
                            int t0 = tsrc[i] + tsrc[i+2] - tsrc[i+1]*2;
                            int t1 = tsrc[i+1];
                            tdst[i] = t0;
                            tdst[i+width] = t1;
                        }
                    }
                }
                else if( ker_width == 5 )
                {
                    for( i = 0; i < width; i++ )
                    {
                        int t0 = tsrc[i] + tsrc[i+4] - tsrc[i+2]*2;
                        int t1 = tsrc[i] + tsrc[i+4] + tsrc[i+2]*6 +
                                 (tsrc[i+1] + tsrc[i+3])*4;
                        tdst[i] = t0;
                        tdst[i+width] = t1;
                    }
                }
                else
                {
                    for( i = 0; i < width; i++ )
                    {
                        int j;
                        int t0 = tsrc[i + ker_x]*fmX0;
                        int t1 = tsrc[i + ker_x]*fmY0;

                        for( j = 1; j <= ker_x; j++ )
                        {
                            t0 += (tsrc[i+ker_x+j] + tsrc[i+ker_x-j])*fmaskX[j];
                            t1 += (tsrc[i+ker_x+j] + tsrc[i+ker_x-j])*fmaskY[j];
                        }

                        tdst[i] = t0;
                        tdst[i+width] = t1;
                    }
                }

                if( !need_copy )
                {
                    /* restore borders */
                    CV_COPY( src - ker_x, tbufc - ker_x, ker_x, x );
                    CV_COPY( src + width, tbufc, ker_right, x );
                }
            }

            if( crows < ker_height )
                src += srcStep;
        }

        if( starting_flag )
        {
            starting_flag = 0;
            trow = rows[ker_y];

            for( i = 0; i < ker_y; i++ )
            {
                tdst = rows[i];
                CV_COPY( tdst, trow, width*2, x );
            }
        }

        /* vertical convolution */
        if( crows != ker_height )
            break;

        tdst2 = dst;

        if( width_rest )
        {
            need_copy = width < CV_MORPH_ALIGN || y == dst_height - 1;

            if( need_copy )
                tdst2 = tbufw;
            else
                CV_COPY( tbufw + width, dst + width, CV_MORPH_ALIGN, x );
        }

        trow = rows[ker_y];

        if( ker_height == 3 )
        {
            int *trow1 = rows[0], *trow2 = rows[2];

            if( y_type == ICV_1_2_1_KERNEL )
            {
                for( x = 0; x < width; x += 4 )
                {
                    int val0, val1;
                    val0 = trow[x]*2 + trow1[x] + trow2[x] -
                           trow[x+width]*2 + trow1[x+width] + trow2[x+width];
                    val1 = trow[x+1]*2 + trow1[x+1] + trow2[x+1] -
                           trow[x+1+width]*2 + trow1[x+1+width] + trow2[x+1+width];

                    tdst2[x + 0] = (short)val0;
                    tdst2[x + 1] = (short)val1;

                    val0 = trow[x+2]*2 + trow1[x+2] + trow2[x+2] -
                           trow[x+2+width]*2 + trow1[x+2+width] + trow2[x+2+width];
                    val1 = trow[x+3]*2 + trow1[x+3] + trow2[x+3] -
                           trow[x+3+width]*2 + trow1[x+3+width] + trow2[x+3+width];

                    tdst2[x + 2] = (short)val0;
                    tdst2[x + 3] = (short)val1;
                }
            }
            else
            {
                for( x = 0; x < width; x += 4 )
                {
                    int val0, val1;
                    val0 = trow[x] -
                           trow[x+width]*2 + trow1[x+width] + trow2[x+width];
                    val1 = trow[x+1] -
                           trow[x+1+width]*2 + trow1[x+1+width] + trow2[x+1+width];

                    tdst2[x + 0] = (short)val0;
                    tdst2[x + 1] = (short)val1;

                    val0 = trow[x+2] -
                           trow[x+2+width]*2 + trow1[x+2+width] + trow2[x+2+width];
                    val1 = trow[x+3] -
                           trow[x+3+width]*2 + trow1[x+3+width] + trow2[x+3+width];

                    tdst2[x + 2] = (short)val0;
                    tdst2[x + 3] = (short)val1;
                }
            }
        }
        else if( ker_height == 5 )
        {
            int *trow0 = rows[0], *trow1 = rows[1], *trow3 = rows[3], *trow4 = rows[4];

            for( x = 0; x < width; x += 4 )
            {
                int val0, val1;
                val0 = trow0[x] + trow4[x] + (trow1[x] + trow3[x])*4 + trow[x]*6 +
                       trow0[x+width] + trow4[x+width] - 2*trow[x+width];
                val1 = trow0[x+1] + trow4[x+1] + (trow1[x+1] + trow3[x+1])*4 + trow[x+1]*6 +
                       trow0[x+1+width] + trow4[x+1+width] - 2*trow[x+1+width];

                tdst2[x + 0] = (short)val0;
                tdst2[x + 1] = (short)val1;

                val0 = trow0[x+2] + trow4[x+2] + (trow1[x+2] + trow3[x+2])*4 + trow[x+2]*6 +
                       trow0[x+2+width] + trow4[x+2+width] - 2*trow[x+2+width];
                val1 = trow0[x+3] + trow4[x+3] + (trow1[x+3] + trow3[x+3])*4 + trow[x+3]*6 +
                       trow0[x+3+width] + trow4[x+3+width] - 2*trow[x+3+width];

                tdst2[x + 2] = (short)val0;
                tdst2[x + 3] = (short)val1;
            }
        }
        else
        {
            for( x = 0; x < width; x += 4 )
            {
                int val0, val1, val2, val3;

                val0 = trow[x]*fmY0 + trow[x + width]*fmX0;
                val1 = trow[x + 1]*fmY0 + trow[x + 1 + width]*fmX0;
                val2 = trow[x + 2]*fmY0 + trow[x + 2 + width]*fmX0;
                val3 = trow[x + 3]*fmY0 + trow[x + 3 + width]*fmX0;

                for( i = 1; i <= ker_y; i++ )
                {
                    int *trow1, *trow2;
                    int m0 = fmaskY[i], m1 = fmaskX[i];
                    trow1 = rows[ker_y - i];
                    trow2 = rows[ker_y + i];
                    val0 += (trow2[x] + trow1[x])*m0 +
                            (trow2[x+width] + trow1[x+width])*m1;
                    val1 += (trow2[x+1] + trow1[x+1])*m0 +
                            (trow2[x+1+width] + trow1[x+1+width])*m1;
                    val2 += (trow2[x+2] + trow1[x+2])*m0 +
                            (trow2[x+2+width] + trow1[x+2+width])*m1;
                    val3 += (trow2[x+3] + trow1[x+3])*m0 +
                            (trow2[x+3+width] + trow1[x+3+width])*m1;
                }

                tdst2[x + 0] = CV_CAST_16S(val0);
                tdst2[x + 1] = CV_CAST_16S(val1);
                tdst2[x + 2] = CV_CAST_16S(val2);
                tdst2[x + 3] = CV_CAST_16S(val3);
            }
        }

        if( width_rest )
        {
            if( need_copy )
                CV_COPY( dst, tbufw, width, x );
            else
                CV_COPY( dst + width, tbufw + width, CV_MORPH_ALIGN, x );
        }

        rows[ker_y] = saved_row;

        /* rotate buffer */
        {
            int *t = rows[0];

            CV_COPY( rows, rows + 1, ker_height - 1, i );
            rows[i] = t;
            crows--;
            dst += dstStep;
        }
    }
    while( ++y < dst_height );

    roiSize->height = y;
    state->crows = crows;

    return CV_OK;
}


static CvStatus CV_STDCALL
icvLaplace_32f_C1R( const float* pSrc, int srcStep,
                    float* dst, int dstStep, CvSize* roiSize,
                    CvFilterState* state, int stage )
{
    float* src = (float*)pSrc;
    int width = roiSize->width;
    int src_height = roiSize->height;
    int dst_height = src_height;
    int x, y = 0, i;

    int ker_width = state->ker_width;
    int ker_height = state->ker_height;
    int ker_x = ker_width/2;
    int ker_y = ker_height/2;
    int ker_right = ker_width - ker_x;

    int crows = state->crows;
    float**rows = (float**)(state->rows);
    float *tbufw = (float*)(state->tbuf);
    float*trow = 0;

    float* fmaskX = (float*)(state->ker0) + ker_x;
    float* fmaskY = (float*)(state->ker1) + ker_y;
    float fmX0 = fmaskX[0], fmY0 = fmaskY[0];

    int is_small_width = width <= ker_width;
    int starting_flag = 0;
    int width_rest = width & (CV_MORPH_ALIGN - 1);
    int y_type = ICV_Y_KERNEL_TYPE(state->kerType);

    if( stage == CV_START + CV_END )
        stage = CV_WHOLE;

    /* initialize cyclic buffer when starting */
    if( stage == CV_WHOLE || stage == CV_START )
    {
        for( i = 0; i < ker_height; i++ )
        {
            rows[i] = (float*)(state->buffer + state->buffer_step * i);
            for( x = width; (x&3) != 0; x++ )
                rows[i][x] = rows[i][x+width] = 0.f;
        }

        crows = ker_y;
        if( stage != CV_WHOLE )
            dst_height -= ker_height - ker_y - 1;
        starting_flag = 1;
    }

    if( stage == CV_END )
        dst_height += ker_height - ker_y - 1;

    srcStep /= sizeof(src[0]);
    dstStep /= sizeof(dst[0]);

    do
    {
        int need_copy = is_small_width | (y == 0);
        float *tsrc;
        float *tdst;
        float *tdst2;
        float *saved_row = rows[ker_y];

        /* fill cyclic buffer - horizontal filtering */
        for( ; crows < ker_height; crows++ )
        {
            tsrc = src - ker_x;
            tdst = rows[crows];

            if( src_height-- <= 0 )
            {
                if( stage != CV_WHOLE && stage != CV_END )
                    break;
                /* duplicate last row */
                trow = rows[crows - 1];
                CV_COPY( tdst, trow, width*2, x );
                continue;
            }

            need_copy |= src_height == 0;

            {
                float* tbufc = (float*)tbufw;

                if( need_copy )
                {
                    tsrc = tbufc - ker_x;
                    CV_COPY( tbufc, src, width, x );
                }
                else
                {
                    CV_COPY( tbufc - ker_x, src - ker_x, ker_x, x );
                    CV_COPY( tbufc, src + width, ker_right, x );
                }

                /* make replication borders */
                {
                float pix = tsrc[ker_x];
                CV_SET( tsrc, pix, ker_x, x );

                pix = tsrc[width + ker_x - 1];
                CV_SET( tsrc + width + ker_x, pix, ker_right, x );
                }

                if( ker_width == 3 )
                {
                    if( y_type == ICV_1_2_1_KERNEL )
                    {
                        for( i = 0; i < width; i++ )
                        {
                            float t0 = tsrc[i] + tsrc[i+2] - tsrc[i+1]*2;
                            float t1 = tsrc[i] + tsrc[i+2] + tsrc[i+1]*2;
                            tdst[i] = t0;
                            tdst[i+width] = t1;
                        }
                    }
                    else
                    {
                        for( i = 0; i < width; i++ )
                        {
                            float t0 = tsrc[i] + tsrc[i+2] - tsrc[i+1]*2;
                            float t1 = tsrc[i+1];
                            tdst[i] = t0;
                            tdst[i+width] = t1;
                        }
                    }
                }
                else if( ker_width == 5 )
                {
                    for( i = 0; i < width; i++ )
                    {
                        float t0 = tsrc[i] + tsrc[i+4] - tsrc[i+2]*2;
                        float t1 = tsrc[i] + tsrc[i+4] + tsrc[i+2]*6 +
                                   (tsrc[i+1] + tsrc[i+3])*4;
                        tdst[i] = t0;
                        tdst[i+width] = t1;
                    }
                }
                else
                {
                    for( i = 0; i < width; i++ )
                    {
                        int j;
                        double t0 = tsrc[i + ker_x]*fmX0;
                        double t1 = tsrc[i + ker_x]*fmY0;

                        for( j = 1; j <= ker_x; j++ )
                        {
                            t0 += (tsrc[i+ker_x+j] + tsrc[i+ker_x-j])*fmaskX[j];
                            t1 += (tsrc[i+ker_x+j] + tsrc[i+ker_x-j])*fmaskY[j];
                        }

                        tdst[i] = (float)t0;
                        tdst[i+width] = (float)t1;
                    }
                }

                if( !need_copy )
                {
                    /* restore borders */
                    CV_COPY( src - ker_x, tbufc - ker_x, ker_x, x );
                    CV_COPY( src + width, tbufc, ker_right, x );
                }
            }

            if( crows < ker_height )
                src += srcStep;
        }

        if( starting_flag )
        {
            starting_flag = 0;
            trow = rows[ker_y];

            for( i = 0; i < ker_y; i++ )
            {
                tdst = rows[i];
                CV_COPY( tdst, trow, width*2, x );
            }
        }

        /* vertical convolution */
        if( crows != ker_height )
            break;

        tdst2 = dst;

        if( width_rest )
        {
            need_copy = width < CV_MORPH_ALIGN || y == dst_height - 1;

            if( need_copy )
                tdst2 = tbufw;
            else
                CV_COPY( tbufw + width, dst + width, CV_MORPH_ALIGN, x );
        }

        trow = rows[ker_y];

        if( ker_height == 3 )
        {
            float* trow1 = rows[0], *trow2 = rows[2];

            if( y_type == ICV_1_2_1_KERNEL )
            {
                for( x = 0; x < width; x += 4 )
                {
                    float val0, val1;
                    val0 = trow[x]*2 + trow1[x] + trow2[x] -
                           trow[x+width]*2 + trow1[x+width] + trow2[x+width];
                    val1 = trow[x+1]*2 + trow1[x+1] + trow2[x+1] -
                           trow[x+1+width]*2 + trow1[x+1+width] + trow2[x+1+width];

                    tdst2[x + 0] = (float)val0;
                    tdst2[x + 1] = (float)val1;

                    val0 = trow[x+2]*2 + trow1[x+2] + trow2[x+2] -
                           trow[x+2+width]*2 + trow1[x+2+width] + trow2[x+2+width];
                    val1 = trow[x+3]*2 + trow1[x+3] + trow2[x+3] -
                           trow[x+3+width]*2 + trow1[x+3+width] + trow2[x+3+width];

                    tdst2[x + 2] = (float)val0;
                    tdst2[x + 3] = (float)val1;
                }
            }
            else
            {
                for( x = 0; x < width; x += 4 )
                {
                    float val0, val1;
                    val0 = trow[x] -
                           trow[x+width]*2 + trow1[x+width] + trow2[x+width];
                    val1 = trow[x+1] -
                           trow[x+1+width]*2 + trow1[x+1+width] + trow2[x+1+width];

                    tdst2[x + 0] = (float)val0;
                    tdst2[x + 1] = (float)val1;

                    val0 = trow[x+2] -
                           trow[x+2+width]*2 + trow1[x+2+width] + trow2[x+2+width];
                    val1 = trow[x+3] -
                           trow[x+3+width]*2 + trow1[x+3+width] + trow2[x+3+width];

                    tdst2[x + 2] = (float)val0;
                    tdst2[x + 3] = (float)val1;
                }
            }
        }
        else if( ker_height == 5 )
        {
            float*trow0 = rows[0], *trow1 = rows[1], *trow3 = rows[3], *trow4 = rows[4];

            for( x = 0; x < width; x += 4 )
            {
                float val0, val1;
                val0 = trow0[x] + trow4[x] + (trow1[x] + trow3[x])*4 + trow[x]*6 +
                       trow0[x+width] + trow4[x+width] - 2*trow[x+width];
                val1 = trow0[x+1] + trow4[x+1] + (trow1[x+1] + trow3[x+1])*4 + trow[x+1]*6 +
                       trow0[x+1+width] + trow4[x+1+width] - 2*trow[x+1+width];

                tdst2[x + 0] = (float)val0;
                tdst2[x + 1] = (float)val1;

                val0 = trow0[x+2] + trow4[x+2] + (trow1[x+2] + trow3[x+2])*4 + trow[x+2]*6 +
                       trow0[x+2+width] + trow4[x+2+width] - 2*trow[x+2+width];
                val1 = trow0[x+3] + trow4[x+3] + (trow1[x+3] + trow3[x+3])*4 + trow[x+3]*6 +
                       trow0[x+3+width] + trow4[x+3+width] - 2*trow[x+3+width];

                tdst2[x + 2] = (float)val0;
                tdst2[x + 3] = (float)val1;
            }
        }
        else
        {
            for( x = 0; x < width; x += 4 )
            {
                double val0, val1, val2, val3;

                val0 = trow[x]*fmY0 + trow[x + width]*fmX0;
                val1 = trow[x + 1]*fmY0 + trow[x + 1 + width]*fmX0;
                val2 = trow[x + 2]*fmY0 + trow[x + 2 + width]*fmX0;
                val3 = trow[x + 3]*fmY0 + trow[x + 3 + width]*fmX0;

                for( i = 1; i <= ker_y; i++ )
                {
                    float *trow1, *trow2;
                    double m0 = fmaskY[i], m1 = fmaskX[i];
                    trow1 = rows[ker_y - i];
                    trow2 = rows[ker_y + i];
                    val0 += (trow2[x] + trow1[x])*m0 +
                            (trow2[x+width] + trow1[x+width])*m1;
                    val1 += (trow2[x+1] + trow1[x+1])*m0 +
                            (trow2[x+1+width] + trow1[x+1+width])*m1;
                    val2 += (trow2[x+2] + trow1[x+2])*m0 +
                            (trow2[x+2+width] + trow1[x+2+width])*m1;
                    val3 += (trow2[x+3] + trow1[x+3])*m0 +
                            (trow2[x+3+width] + trow1[x+3+width])*m1;
                }

                tdst2[x + 0] = (float)val0;
                tdst2[x + 1] = (float)val1;
                tdst2[x + 2] = (float)val2;
                tdst2[x + 3] = (float)val3;
            }
        }

        if( width_rest )
        {
            if( need_copy )
                CV_COPY( dst, tbufw, width, x );
            else
                CV_COPY( dst + width, tbufw + width, CV_MORPH_ALIGN, x );
        }

        rows[ker_y] = saved_row;

        /* rotate buffer */
        {
            float*t = rows[0];

            CV_COPY( rows, rows + 1, ker_height - 1, i );
            rows[i] = t;
            crows--;
            dst += dstStep;
        }
    }
    while( ++y < dst_height );

    roiSize->height = y;
    state->crows = crows;

    return CV_OK;
}


/****************************************************************************************/

/* lightweight convolution with 3x3 kernel */
void icvSepConvSmall3_32f( float*  src, int src_step,
                           float*  dst, int dst_step,
                           CvSize src_size,
                           const float* kx, const float* ky,
                           float*  buffer )
{
    int  dst_width, buffer_step = 0;
    int  x, y;

    assert( src && dst && src_size.width > 2 && src_size.height > 2 &&
            (src_step & 3) == 0 && (dst_step & 3) == 0 &&
            (kx || ky) && (buffer || !kx || !ky));

    src_step >>= 2;
    dst_step >>= 2;

    dst_width = src_size.width - 2;

    if( !kx )
    {
        /* set vars, so that vertical convolution
           will write results into destination ROI and
           horizontal convolution won't run */
        src_size.width = dst_width;
        buffer_step = dst_step;
        buffer = dst;
        dst_width = 0;
    }

    assert( src_step >= src_size.width && dst_step >= dst_width );

    src_size.height -= 3;
    if( !ky )
    {
        /* set vars, so that vertical convolution won't run and
           horizontal convolution will write results into destination ROI and */
        src_size.height += 3;
        buffer_step = src_step;
        buffer = src;
        src_size.width = 0;
    }

    for( y = 0; y <= src_size.height; y++, src += src_step,
                                           dst += dst_step,
                                           buffer += buffer_step )
    {
        float* src2 = src + src_step;
        float* src3 = src + src_step*2;
        for( x = 0; x < src_size.width; x++ )
        {
            buffer[x] = (float)(ky[0]*src[x] + ky[1]*src2[x] + ky[2]*src3[x]);
        }

        for( x = 0; x < dst_width; x++ )
        {
            dst[x] = (float)(kx[0]*buffer[x] + kx[1]*buffer[x+1] + kx[2]*buffer[x+2]);
        }
    }
}


/****************************************************************************************\
*                                   External Functions                                   *
\****************************************************************************************/

#define  ICV_DEF_INIT_DERIV_TAB( FUNCNAME )                \
static  void  icvInit##FUNCNAME##Table( CvFuncTable* tab ) \
{                                                          \
    tab->fn_2d[CV_8U] = (void*)icv##FUNCNAME##_8u16s_C1R;  \
    tab->fn_2d[CV_8S] = 0;                                 \
    tab->fn_2d[CV_32F] = (void*)icv##FUNCNAME##_32f_C1R;   \
}

ICV_DEF_INIT_DERIV_TAB( Sobel )
ICV_DEF_INIT_DERIV_TAB( Scharr )
ICV_DEF_INIT_DERIV_TAB( Laplace )


////////////////////////////////// IPP derivative filters ////////////////////////////////

icvFilterSobelVert_8u16s_C1R_t icvFilterSobelVert_8u16s_C1R_p = 0;
icvFilterSobelHoriz_8u16s_C1R_t icvFilterSobelHoriz_8u16s_C1R_p = 0;
icvFilterSobelVertSecond_8u16s_C1R_t icvFilterSobelVertSecond_8u16s_C1R_p = 0;
icvFilterSobelHorizSecond_8u16s_C1R_t icvFilterSobelHorizSecond_8u16s_C1R_p = 0;
icvFilterSobelCross_8u16s_C1R_t icvFilterSobelCross_8u16s_C1R_p = 0;

icvFilterSobelVert_32f_C1R_t icvFilterSobelVert_32f_C1R_p = 0;
icvFilterSobelHoriz_32f_C1R_t icvFilterSobelHoriz_32f_C1R_p = 0;
icvFilterSobelVertSecond_32f_C1R_t icvFilterSobelVertSecond_32f_C1R_p = 0;
icvFilterSobelHorizSecond_32f_C1R_t icvFilterSobelHorizSecond_32f_C1R_p = 0;
icvFilterSobelCross_32f_C1R_t icvFilterSobelCross_32f_C1R_p = 0;

icvFilterScharrVert_8u16s_C1R_t icvFilterScharrVert_8u16s_C1R_p = 0;
icvFilterScharrHoriz_8u16s_C1R_t icvFilterScharrHoriz_8u16s_C1R_p = 0;
icvFilterScharrVert_32f_C1R_t icvFilterScharrVert_32f_C1R_p = 0;
icvFilterScharrHoriz_32f_C1R_t icvFilterScharrHoriz_32f_C1R_p = 0;

//////////////////////////////////////////////////////////////////////////////////////////

CV_IMPL void
cvSobel( const void* srcarr, void* dstarr, int dx, int dy, int aperture_size )
{
    static CvFuncTable sobel_tab, scharr_tab;
    static int inittab = 0;
    CvMat* temp = 0;

    CvFilterState *state = 0;
    CV_FUNCNAME( "cvSobel" );

    __BEGIN__;

    int origin = 0;
    int depth;
    CvMat srcstub, *src = (CvMat*)srcarr;
    CvMat dststub, *dst = (CvMat*)dstarr;
    CvFilterFunc func = 0;
    CvSize size;
    int datatype;

    if( !inittab )
    {
        icvInitSobelTable( &sobel_tab );
        icvInitScharrTable( &scharr_tab );
        inittab = 1;
    }

    CV_CALL( src = cvGetMat( src, &srcstub ));
    CV_CALL( dst = cvGetMat( dst, &dststub ));

    if( CV_IS_IMAGE_HDR( srcarr ))
        origin = ((IplImage*)srcarr)->origin;

    if( CV_MAT_CN( src->type ) != 1 || CV_MAT_CN( dst->type ) != 1 )
        CV_ERROR( CV_BadNumChannels, cvUnsupportedFormat );

    if( CV_MAT_DEPTH( src->type ) == CV_8U )
    {
        if( CV_MAT_DEPTH( dst->type ) != CV_16S )
            CV_ERROR( CV_StsUnmatchedFormats,
            "Destination array should be 16-bit signed when the source is 8-bit" );
    }
    else if( CV_MAT_DEPTH( src->type ) == CV_32F )
    {
        if( CV_MAT_DEPTH( dst->type ) != CV_32F )
            CV_ERROR( CV_StsUnmatchedFormats,
            "Destination array should be 32-bit floating point "
            "when the source is 32-bit floating point" );
    }
    else
        CV_ERROR( CV_StsUnsupportedFormat, "Unsupported source array format" );

    depth = CV_MAT_DEPTH( src->type );
    datatype = icvDepthToDataType(depth);

    if( !CV_ARE_SIZES_EQ( src, dst ))
        CV_ERROR( CV_StsBadArg, "src and dst have different sizes" );

    size = cvGetMatSize(src);

    if( ((aperture_size == CV_SCHARR || aperture_size == 3 || aperture_size == 5) &&
        dx <= 1 && dy <= 1 && icvFilterSobelVert_8u16s_C1R_p))
    {
        CvSobelFixedIPPFunc ipp_sobel_func = 0;
        CvFilterFixedIPPFunc ipp_scharr_func = 0;

        if( dx == 1 && dy == 0 && aperture_size == CV_SCHARR )
            ipp_scharr_func = depth == CV_8U ?
                icvFilterScharrVert_8u16s_C1R_p : icvFilterScharrVert_32f_C1R_p;
        else if( dx == 0 && dy == 1 && aperture_size == CV_SCHARR )
            ipp_scharr_func = depth == CV_8U ?
                icvFilterScharrHoriz_8u16s_C1R_p : icvFilterScharrHoriz_32f_C1R_p;
        else if( dx == 1 && dy == 0 )
            ipp_sobel_func = depth == CV_8U ?
                icvFilterSobelVert_8u16s_C1R_p : icvFilterSobelVert_32f_C1R_p;
        else if( dx == 0 && dy == 1 )
            ipp_sobel_func = depth == CV_8U ?
                icvFilterSobelHoriz_8u16s_C1R_p : icvFilterSobelHoriz_32f_C1R_p;
        else if( dx == 2 && dy == 0 )
            ipp_sobel_func = depth == CV_8U ?
                icvFilterSobelVertSecond_8u16s_C1R_p : icvFilterSobelVertSecond_32f_C1R_p;
        else if( dx == 1 && dy == 1 )
            ipp_sobel_func = depth == CV_8U ?
                icvFilterSobelCross_8u16s_C1R_p : icvFilterSobelCross_32f_C1R_p;
        else if( dx == 0 && dy == 2 )
            ipp_sobel_func = depth == CV_8U ?
                icvFilterSobelHorizSecond_8u16s_C1R_p : icvFilterSobelHorizSecond_32f_C1R_p;

        if( ipp_sobel_func || ipp_scharr_func )
        {
            int need_to_negate = (dx == 1 && aperture_size != CV_SCHARR) ^ ((dy == 1) && origin);
            aperture_size = aperture_size == CV_SCHARR ? 3 : aperture_size;
            CvSize el_size = { aperture_size, aperture_size };
            CvPoint el_anchor = { aperture_size/2, aperture_size/2 };
            int stripe_buf_size = 1 << 15; // the optimal value may depend on CPU cache,
                                           // overhead of current IPP code etc.
            const uchar* shifted_ptr;
            int y, delta_y = 0;
            int temp_step;
            int dst_step = dst->step ? dst->step : CV_STUB_STEP;
            CvSize stripe_size;

            CV_CALL( temp = icvIPPFilterInit( src, stripe_buf_size, el_size ));

            shifted_ptr = temp->data.ptr +
                el_anchor.y*temp->step + el_anchor.x*CV_ELEM_SIZE(depth);
            temp_step = temp->step ? temp->step : CV_STUB_STEP;

            for( y = 0; y < src->rows; y += delta_y )
            {
                delta_y = icvIPPFilterNextStripe( src, temp, y, el_size, el_anchor );
                stripe_size.width = size.width;
                stripe_size.height = delta_y;

                if( ipp_sobel_func )
                {
                    IPPI_CALL( ipp_sobel_func( shifted_ptr, temp_step,
                            dst->data.ptr + y*dst_step, dst_step,
                            stripe_size, aperture_size*10 + aperture_size ));
                }
                else
                {
                    IPPI_CALL( ipp_scharr_func( shifted_ptr, temp_step,
                            dst->data.ptr + y*dst_step, dst_step, stripe_size ));
                }
            }

            if( need_to_negate )
                cvSubRS( dst, cvScalarAll(0), dst );
            EXIT;
        }
    }

    if( aperture_size == CV_SCHARR )
    {
        CV_CALL( state = icvScharrInitAlloc( src->width, datatype, origin, dx, dy ));
        func = (CvFilterFunc)(scharr_tab.fn_2d[depth]);
    }
    else
    {
        CV_CALL( state = icvSobelInitAlloc( src->width, datatype, aperture_size, origin, dx, dy ));
        func = (CvFilterFunc)(sobel_tab.fn_2d[depth]);
    }

    if( !func )
        CV_ERROR( CV_StsUnsupportedFormat, "" );

    IPPI_CALL( func( src->data.ptr, src->step, dst->data.ptr,
                     dst->step, &size, state, 0 ));

    __END__;

    cvReleaseMat( &temp );
    icvFilterFree( &state );
}


CV_IMPL void
cvLaplace( const void* srcarr, void* dstarr, int aperture_size )
{
    static CvFuncTable laplace_tab;
    static int inittab = 0;

    CV_FUNCNAME( "cvLaplace" );

    CvFilterState *state = 0;

    __BEGIN__;

    int depth;
    CvMat srcstub, *src = (CvMat*)srcarr;
    CvMat dststub, *dst = (CvMat*)dstarr;
    CvFilterFunc func = 0;
    CvSize size;

    if( !inittab )
    {
        icvInitLaplaceTable( &laplace_tab );
        inittab = 1;
    }

    CV_CALL( src = cvGetMat( src, &srcstub ));
    CV_CALL( dst = cvGetMat( dst, &dststub ));

    if( CV_MAT_CN( src->type ) != 1 || CV_MAT_CN( dst->type ) != 1 )
        CV_ERROR( CV_BadNumChannels, cvUnsupportedFormat );

    if( CV_MAT_DEPTH( src->type ) == CV_8U )
    {
        if( CV_MAT_DEPTH( dst->type ) != CV_16S )
            CV_ERROR( CV_StsUnmatchedFormats, "Destination should have 16s format"
                                              " when source has 8u or 8s format" );
    }
    else if( CV_MAT_DEPTH( src->type ) == CV_32F )
    {
        if( CV_MAT_DEPTH( dst->type ) != CV_32F )
            CV_ERROR( CV_StsUnmatchedFormats, "Destination should have 16s format"
                                              " when source has 8u or 8s format" );
    }
    else
    {
        CV_ERROR( CV_StsUnsupportedFormat, "Unsupported source array format" );
    }

    depth = CV_MAT_DEPTH( src->type );

    if( !CV_ARE_SIZES_EQ( src, dst ))
        CV_ERROR( CV_StsBadArg, "src and dst have different sizes" );

    CV_CALL( state = icvLaplaceInitAlloc( src->width, icvDepthToDataType(depth), aperture_size ));
    func = (CvFilterFunc)(laplace_tab.fn_2d[depth]);

    if( !func )
        CV_ERROR( CV_StsUnsupportedFormat, "" );

    size = cvGetMatSize(src);
    IPPI_CALL( func( src->data.ptr, src->step, dst->data.ptr,
                     dst->step, &size, state, 0 ));

    __END__;

    icvFilterFree( &state );
}

/* End of file. */


