// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html

#include "precomp.hpp"
#include "opencl_kernels_imgproc.hpp"
#include "color.hpp"

namespace cv
{

#ifdef HAVE_OPENCL

static bool ocl_cvtColor( InputArray _src, OutputArray _dst, int code, int dcn )
{
    int bidx = swapBlue(code) ? 2 : 0;

    switch (code)
    {
    case COLOR_BGR2BGRA: case COLOR_RGB2BGRA: case COLOR_BGRA2BGR:
    case COLOR_RGBA2BGR: case COLOR_RGB2BGR: case COLOR_BGRA2RGBA:
    {
        bool reverse = !(code == COLOR_BGR2BGRA || code == COLOR_BGRA2BGR);
        return oclCvtColorBGR2BGR(_src, _dst, dcn, reverse);
    }
    case COLOR_BGR5652BGR: case COLOR_BGR5552BGR: case COLOR_BGR5652RGB: case COLOR_BGR5552RGB:
    case COLOR_BGR5652BGRA: case COLOR_BGR5552BGRA: case COLOR_BGR5652RGBA: case COLOR_BGR5552RGBA:
        return oclCvtColor5x52BGR(_src, _dst, dcn, bidx, greenBits(code));

    case COLOR_BGR2BGR565: case COLOR_BGR2BGR555: case COLOR_RGB2BGR565: case COLOR_RGB2BGR555:
    case COLOR_BGRA2BGR565: case COLOR_BGRA2BGR555: case COLOR_RGBA2BGR565: case COLOR_RGBA2BGR555:
        return oclCvtColorBGR25x5(_src, _dst, bidx, greenBits(code) );

    case COLOR_BGR5652GRAY: case COLOR_BGR5552GRAY:
        return oclCvtColor5x52Gray(_src, _dst, greenBits(code));

    case COLOR_GRAY2BGR565: case COLOR_GRAY2BGR555:
        return oclCvtColorGray25x5(_src, _dst, greenBits(code));

    case COLOR_BGR2GRAY: case COLOR_BGRA2GRAY:
    case COLOR_RGB2GRAY: case COLOR_RGBA2GRAY:
        return oclCvtColorBGR2Gray(_src, _dst, bidx);

    case COLOR_GRAY2BGR:
    case COLOR_GRAY2BGRA:
        return oclCvtColorGray2BGR(_src, _dst, dcn);

    case COLOR_BGR2YUV:
    case COLOR_RGB2YUV:
        return oclCvtColorBGR2YUV(_src, _dst, bidx);

    case COLOR_YUV2BGR:
    case COLOR_YUV2RGB:
        return oclCvtColorYUV2BGR(_src, _dst, dcn, bidx);

    case COLOR_YUV2RGB_NV12: case COLOR_YUV2BGR_NV12: case COLOR_YUV2RGB_NV21: case COLOR_YUV2BGR_NV21:
    case COLOR_YUV2RGBA_NV12: case COLOR_YUV2BGRA_NV12: case COLOR_YUV2RGBA_NV21: case COLOR_YUV2BGRA_NV21:
    {
        int uidx = code == COLOR_YUV2RGBA_NV21 || code == COLOR_YUV2RGB_NV21 ||
                   code == COLOR_YUV2BGRA_NV21 || code == COLOR_YUV2BGR_NV21 ? 1 : 0;
        return oclCvtColorTwoPlaneYUV2BGR(_src, _dst, dcn, bidx, uidx);
    }
    case COLOR_YUV2BGR_YV12: case COLOR_YUV2RGB_YV12: case COLOR_YUV2BGRA_YV12: case COLOR_YUV2RGBA_YV12:
    case COLOR_YUV2BGR_IYUV: case COLOR_YUV2RGB_IYUV: case COLOR_YUV2BGRA_IYUV: case COLOR_YUV2RGBA_IYUV:
    {
        int uidx = code == COLOR_YUV2BGRA_YV12 || code == COLOR_YUV2BGR_YV12 ||
                   code == COLOR_YUV2RGBA_YV12 || code == COLOR_YUV2RGB_YV12 ? 1 : 0;
        return oclCvtColorThreePlaneYUV2BGR(_src, _dst, dcn, bidx, uidx);
    }
    case COLOR_YUV2GRAY_420:
    {
        return oclCvtColorYUV2Gray_420(_src, _dst);
    }
    case COLOR_RGB2YUV_YV12: case COLOR_BGR2YUV_YV12: case COLOR_RGBA2YUV_YV12: case COLOR_BGRA2YUV_YV12:
    case COLOR_RGB2YUV_IYUV: case COLOR_BGR2YUV_IYUV: case COLOR_RGBA2YUV_IYUV: case COLOR_BGRA2YUV_IYUV:
    {
        int uidx = code == COLOR_RGBA2YUV_YV12 || code == COLOR_RGB2YUV_YV12 ||
                   code == COLOR_BGRA2YUV_YV12 || code == COLOR_BGR2YUV_YV12 ? 1 : 0;
        return oclCvtColorBGR2ThreePlaneYUV(_src, _dst, bidx, uidx );
    }
    case COLOR_YUV2RGB_UYVY: case COLOR_YUV2BGR_UYVY: case COLOR_YUV2RGBA_UYVY: case COLOR_YUV2BGRA_UYVY:
    case COLOR_YUV2RGB_YUY2: case COLOR_YUV2BGR_YUY2: case COLOR_YUV2RGB_YVYU: case COLOR_YUV2BGR_YVYU:
    case COLOR_YUV2RGBA_YUY2: case COLOR_YUV2BGRA_YUY2: case COLOR_YUV2RGBA_YVYU: case COLOR_YUV2BGRA_YVYU:
    {
        int yidx = (code==COLOR_YUV2RGB_UYVY || code==COLOR_YUV2RGBA_UYVY ||
                    code==COLOR_YUV2BGR_UYVY || code==COLOR_YUV2BGRA_UYVY) ? 1 : 0;
        int uidx = (code==COLOR_YUV2RGB_YVYU || code==COLOR_YUV2RGBA_YVYU ||
                    code==COLOR_YUV2BGR_YVYU || code==COLOR_YUV2BGRA_YVYU) ? 2 : 0;
        uidx = 1 - yidx + uidx;

        return oclCvtColorOnePlaneYUV2BGR(_src, _dst, dcn, bidx, uidx, yidx);
    }
    case COLOR_BGR2YCrCb:
    case COLOR_RGB2YCrCb:
        return oclCvtColorBGR2YCrCb(_src, _dst, bidx);

    case COLOR_YCrCb2BGR:
    case COLOR_YCrCb2RGB:
        return oclCvtcolorYCrCb2BGR(_src, _dst, dcn, bidx);

    case COLOR_BGR2XYZ:
    case COLOR_RGB2XYZ:
        return oclCvtColorBGR2XYZ(_src, _dst, bidx);

    case COLOR_XYZ2BGR:
    case COLOR_XYZ2RGB:
        return oclCvtColorXYZ2BGR(_src, _dst, dcn, bidx);

    case COLOR_BGR2HSV: case COLOR_BGR2HSV_FULL:
    case COLOR_RGB2HSV: case COLOR_RGB2HSV_FULL:
        return oclCvtColorBGR2HSV(_src, _dst, bidx, isFullRangeHSV(code));

    case COLOR_BGR2HLS: case COLOR_BGR2HLS_FULL:
    case COLOR_RGB2HLS: case COLOR_RGB2HLS_FULL:
        return oclCvtColorBGR2HLS(_src, _dst, bidx, isFullRangeHSV(code));

    case COLOR_HSV2BGR: case COLOR_HSV2BGR_FULL:
    case COLOR_HSV2RGB: case COLOR_HSV2RGB_FULL:
        return oclCvtColorHSV2BGR(_src, _dst, dcn, bidx, isFullRangeHSV(code));

    case COLOR_HLS2BGR: case COLOR_HLS2BGR_FULL:
    case COLOR_HLS2RGB: case COLOR_HLS2RGB_FULL:
        return oclCvtColorHLS2BGR(_src, _dst, dcn, bidx, isFullRangeHSV(code));

    case COLOR_RGBA2mRGBA:
        return oclCvtColorRGBA2mRGBA(_src, _dst);

    case COLOR_mRGBA2RGBA:
        return oclCvtColormRGBA2RGBA(_src, _dst);

    case COLOR_BGR2Lab: case COLOR_LBGR2Lab:
    case COLOR_RGB2Lab: case COLOR_LRGB2Lab:
        return oclCvtColorBGR2Lab(_src, _dst, bidx, is_sRGB(code));

    case COLOR_BGR2Luv: case COLOR_LBGR2Luv:
    case COLOR_RGB2Luv: case COLOR_LRGB2Luv:
        return oclCvtColorBGR2Luv(_src, _dst, bidx, is_sRGB(code));

    case COLOR_Lab2BGR: case COLOR_Lab2LBGR:
    case COLOR_Lab2RGB: case COLOR_Lab2LRGB:
        return oclCvtColorLab2BGR(_src, _dst, dcn, bidx, is_sRGB(code));

    case COLOR_Luv2BGR: case COLOR_Luv2LBGR:
    case COLOR_Luv2RGB: case COLOR_Luv2LRGB:
        return oclCvtColorLuv2BGR(_src, _dst, dcn, bidx, is_sRGB(code));

    default:
        return false;
    }
}

#endif


// helper function for dual-plane modes

void cvtColorTwoPlane( InputArray _ysrc, InputArray _uvsrc, OutputArray _dst, int code )
{
    // only YUV420 is currently supported
    switch (code)
    {
        case COLOR_YUV2BGR_NV21:  case COLOR_YUV2RGB_NV21:  case COLOR_YUV2BGR_NV12:  case COLOR_YUV2RGB_NV12:
        case COLOR_YUV2BGRA_NV21: case COLOR_YUV2RGBA_NV21: case COLOR_YUV2BGRA_NV12: case COLOR_YUV2RGBA_NV12:
            break;
        default:
            CV_Error( CV_StsBadFlag, "Unknown/unsupported color conversion code" );
            return;
    }

    cvtColorTwoPlaneYUV2BGRpair(_ysrc, _uvsrc, _dst, dstChannels(code), swapBlue(code), uIndex(code));
}


//////////////////////////////////////////////////////////////////////////////////////////
//                                   The main function                                  //
//////////////////////////////////////////////////////////////////////////////////////////

void cvtColor( InputArray _src, OutputArray _dst, int code, int dcn )
{
    CV_INSTRUMENT_REGION();

    CV_Assert(!_src.empty());

    if(dcn <= 0)
            dcn = dstChannels(code);

    CV_OCL_RUN( _src.dims() <= 2 && _dst.isUMat() &&
                !(CV_MAT_DEPTH(_src.type()) == CV_8U && (code == COLOR_Luv2BGR || code == COLOR_Luv2RGB)),
                ocl_cvtColor(_src, _dst, code, dcn) )

    switch( code )
    {
        case COLOR_BGR2BGRA: case COLOR_RGB2BGRA: case COLOR_BGRA2BGR:
        case COLOR_RGBA2BGR: case COLOR_RGB2BGR:  case COLOR_BGRA2RGBA:
            if(_src.channels() == 1)
                cvtColorGray2BGR(_src, _dst, dcn);
            else
                cvtColorBGR2BGR(_src, _dst, dcn, swapBlue(code));
            break;

        case COLOR_BGR2BGR565:  case COLOR_BGR2BGR555: case COLOR_BGRA2BGR565: case COLOR_BGRA2BGR555:
        case COLOR_RGB2BGR565:  case COLOR_RGB2BGR555: case COLOR_RGBA2BGR565: case COLOR_RGBA2BGR555:
            cvtColorBGR25x5(_src, _dst, swapBlue(code), greenBits(code));
            break;

        case COLOR_BGR5652BGR:  case COLOR_BGR5552BGR: case COLOR_BGR5652BGRA: case COLOR_BGR5552BGRA:
        case COLOR_BGR5652RGB:  case COLOR_BGR5552RGB: case COLOR_BGR5652RGBA: case COLOR_BGR5552RGBA:
            cvtColor5x52BGR(_src, _dst, dcn, swapBlue(code), greenBits(code));
            break;

        case COLOR_BGR2GRAY: case COLOR_BGRA2GRAY:
        case COLOR_RGB2GRAY: case COLOR_RGBA2GRAY:
            cvtColorBGR2Gray(_src, _dst, swapBlue(code));
            break;

        case COLOR_BGR5652GRAY:
        case COLOR_BGR5552GRAY:
            cvtColor5x52Gray(_src, _dst, greenBits(code));
            break;

        case COLOR_GRAY2BGR:
        case COLOR_GRAY2BGRA:
            cvtColorGray2BGR(_src, _dst, dcn);
            break;

        case COLOR_GRAY2BGR565:
        case COLOR_GRAY2BGR555:
            cvtColorGray25x5(_src, _dst, greenBits(code));
            break;

        case COLOR_BGR2YCrCb: case COLOR_RGB2YCrCb:
        case COLOR_BGR2YUV:   case COLOR_RGB2YUV:
            cvtColorBGR2YUV(_src, _dst, swapBlue(code), code == COLOR_BGR2YCrCb || code == COLOR_RGB2YCrCb);
            break;

        case COLOR_YCrCb2BGR: case COLOR_YCrCb2RGB:
        case COLOR_YUV2BGR:   case COLOR_YUV2RGB:
            cvtColorYUV2BGR(_src, _dst, dcn, swapBlue(code), code == COLOR_YCrCb2BGR || code == COLOR_YCrCb2RGB);
            break;

        case COLOR_BGR2XYZ:
        case COLOR_RGB2XYZ:
            cvtColorBGR2XYZ(_src, _dst, swapBlue(code));
            break;

        case COLOR_XYZ2BGR:
        case COLOR_XYZ2RGB:
            cvtColorXYZ2BGR(_src, _dst, dcn, swapBlue(code));
            break;

        case COLOR_BGR2HSV: case COLOR_BGR2HSV_FULL:
        case COLOR_RGB2HSV: case COLOR_RGB2HSV_FULL:
            cvtColorBGR2HSV(_src, _dst, swapBlue(code), isFullRangeHSV(code));
            break;

        case COLOR_BGR2HLS: case COLOR_BGR2HLS_FULL:
        case COLOR_RGB2HLS: case COLOR_RGB2HLS_FULL:
            cvtColorBGR2HLS(_src, _dst, swapBlue(code), isFullRangeHSV(code));
            break;

        case COLOR_HSV2BGR: case COLOR_HSV2BGR_FULL:
        case COLOR_HSV2RGB: case COLOR_HSV2RGB_FULL:
            cvtColorHSV2BGR(_src, _dst, dcn, swapBlue(code), isFullRangeHSV(code));
            break;

        case COLOR_HLS2BGR: case COLOR_HLS2BGR_FULL:
        case COLOR_HLS2RGB: case COLOR_HLS2RGB_FULL:
            cvtColorHLS2BGR(_src, _dst, dcn, swapBlue(code), isFullRangeHSV(code));
            break;

        case COLOR_BGR2Lab: case COLOR_LBGR2Lab:
        case COLOR_RGB2Lab: case COLOR_LRGB2Lab:
            cvtColorBGR2Lab(_src, _dst, swapBlue(code), is_sRGB(code));
            break;

        case COLOR_BGR2Luv: case COLOR_LBGR2Luv:
        case COLOR_RGB2Luv: case COLOR_LRGB2Luv:
            cvtColorBGR2Luv(_src, _dst, swapBlue(code), is_sRGB(code));
            break;

        case COLOR_Lab2BGR: case COLOR_Lab2LBGR:
        case COLOR_Lab2RGB: case COLOR_Lab2LRGB:
            cvtColorLab2BGR(_src, _dst, dcn, swapBlue(code), is_sRGB(code));
            break;

        case COLOR_Luv2BGR: case COLOR_Luv2LBGR:
        case COLOR_Luv2RGB: case COLOR_Luv2LRGB:
            cvtColorLuv2BGR(_src, _dst, dcn, swapBlue(code), is_sRGB(code));
            break;

        case COLOR_BayerBG2GRAY: case COLOR_BayerGB2GRAY: case COLOR_BayerRG2GRAY: case COLOR_BayerGR2GRAY:
        case COLOR_BayerBG2BGR: case COLOR_BayerGB2BGR: case COLOR_BayerRG2BGR: case COLOR_BayerGR2BGR:
        case COLOR_BayerBG2BGR_VNG: case COLOR_BayerGB2BGR_VNG: case COLOR_BayerRG2BGR_VNG: case COLOR_BayerGR2BGR_VNG:
        case COLOR_BayerBG2BGR_EA: case COLOR_BayerGB2BGR_EA: case COLOR_BayerRG2BGR_EA: case COLOR_BayerGR2BGR_EA:
        case COLOR_BayerBG2BGRA: case COLOR_BayerGB2BGRA: case COLOR_BayerRG2BGRA: case COLOR_BayerGR2BGRA:
            {
                Mat src;
                if (_src.getObj() == _dst.getObj()) // inplace processing (#6653)
                    _src.copyTo(src);
                else
                    src = _src.getMat();
                demosaicing(src, _dst, code, dcn);
                break;
            }

        case COLOR_YUV2BGR_NV21:  case COLOR_YUV2RGB_NV21:  case COLOR_YUV2BGR_NV12:  case COLOR_YUV2RGB_NV12:
        case COLOR_YUV2BGRA_NV21: case COLOR_YUV2RGBA_NV21: case COLOR_YUV2BGRA_NV12: case COLOR_YUV2RGBA_NV12:
            // http://www.fourcc.org/yuv.php#NV21 == yuv420sp -> a plane of 8 bit Y samples followed by an interleaved V/U plane containing 8 bit 2x2 subsampled chroma samples
            // http://www.fourcc.org/yuv.php#NV12 -> a plane of 8 bit Y samples followed by an interleaved U/V plane containing 8 bit 2x2 subsampled colour difference samples
            cvtColorTwoPlaneYUV2BGR(_src, _dst, dcn, swapBlue(code), uIndex(code));
            break;

        case COLOR_YUV2BGR_YV12: case COLOR_YUV2RGB_YV12: case COLOR_YUV2BGRA_YV12: case COLOR_YUV2RGBA_YV12:
        case COLOR_YUV2BGR_IYUV: case COLOR_YUV2RGB_IYUV: case COLOR_YUV2BGRA_IYUV: case COLOR_YUV2RGBA_IYUV:
            //http://www.fourcc.org/yuv.php#YV12 == yuv420p -> It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes.
            //http://www.fourcc.org/yuv.php#IYUV == I420 -> It comprises an NxN Y plane followed by (N/2)x(N/2) U and V planes
            cvtColorThreePlaneYUV2BGR(_src, _dst, dcn, swapBlue(code), uIndex(code));
            break;

        case COLOR_YUV2GRAY_420:
            cvtColorYUV2Gray_420(_src, _dst);
            break;

        case COLOR_RGB2YUV_YV12: case COLOR_BGR2YUV_YV12: case COLOR_RGBA2YUV_YV12: case COLOR_BGRA2YUV_YV12:
        case COLOR_RGB2YUV_IYUV: case COLOR_BGR2YUV_IYUV: case COLOR_RGBA2YUV_IYUV: case COLOR_BGRA2YUV_IYUV:
            cvtColorBGR2ThreePlaneYUV(_src, _dst, swapBlue(code), uIndex(code));
            break;

        case COLOR_YUV2RGB_UYVY: case COLOR_YUV2BGR_UYVY: case COLOR_YUV2RGBA_UYVY: case COLOR_YUV2BGRA_UYVY:
        case COLOR_YUV2RGB_YUY2: case COLOR_YUV2BGR_YUY2: case COLOR_YUV2RGB_YVYU: case COLOR_YUV2BGR_YVYU:
        case COLOR_YUV2RGBA_YUY2: case COLOR_YUV2BGRA_YUY2: case COLOR_YUV2RGBA_YVYU: case COLOR_YUV2BGRA_YVYU:
            //http://www.fourcc.org/yuv.php#UYVY
            //http://www.fourcc.org/yuv.php#YUY2
            //http://www.fourcc.org/yuv.php#YVYU
            {
                int ycn  = (code==COLOR_YUV2RGB_UYVY || code==COLOR_YUV2BGR_UYVY ||
                            code==COLOR_YUV2RGBA_UYVY || code==COLOR_YUV2BGRA_UYVY) ? 1 : 0;
                cvtColorOnePlaneYUV2BGR(_src, _dst, dcn, swapBlue(code), uIndex(code), ycn);
                break;
            }

        case COLOR_YUV2GRAY_UYVY:
        case COLOR_YUV2GRAY_YUY2:
            cvtColorYUV2Gray_ch(_src, _dst, code == COLOR_YUV2GRAY_UYVY ? 1 : 0);
            break;

        case COLOR_RGBA2mRGBA:
            cvtColorRGBA2mRGBA(_src, _dst);
            break;

        case COLOR_mRGBA2RGBA:
            cvtColormRGBA2RGBA(_src, _dst);
            break;
        default:
            CV_Error( CV_StsBadFlag, "Unknown/unsupported color conversion code" );
    }
}
} //namespace cv


CV_IMPL void
cvCvtColor( const CvArr* srcarr, CvArr* dstarr, int code )
{
    cv::Mat src = cv::cvarrToMat(srcarr), dst0 = cv::cvarrToMat(dstarr), dst = dst0;
    CV_Assert( src.depth() == dst.depth() );

    cv::cvtColor(src, dst, code, dst.channels());
    CV_Assert( dst.data == dst0.data );
}
