/** @file */
/* The MIT License
* 
* Copyright (c) 2008, Naotoshi Seo <sonots(at)sonots.com>
* 
* 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 CV_CROPIMAGEROI_INCLUDED
#define CV_CROPIMAGEROI_INCLUDED

#include "cv.h"
#include "cvaux.h"
#include "cxcore.h"
#define _USE_MATH_DEFINES
#include <math.h>

#include "cvrect32f.h"
#include "cvcreateaffine.h"
#include "cvcreateaffineimage.h"

// CVAPI(void) cvPutImageROI( const IplImage* src,
//                     IplImage* dst,
//                     CvRect32f rect32f = cvRect32f(0,0,1,1,0),
//                     CvPoint2D32f shear = cvPoint2D32f(0,0),
//                     const IplImage* mask = NULL,
//                     bool circumscribe = 0 );

/**
 * Put a source image on the specified region on a target image 
 *
 * Use CvBox32f to define rotation center as the center of rectangle,
 * and use cvRect32fBox32( box32f ) to pass argument. 
 *
 * @param src          The source image
 * @param dst          The target image
 * @param rect32f      The rectangle region (x,y,width,height) to put
 *                     the rotation angle in degree where the rotation center is (x,y)
 * @param shear        The shear deformation parameter shx and shy
 * @param mask         The mask image
 * @param circumscribe Put a circular (ellipsoidal) image as a circumscribed 
 *                     circle (ellipsoid) rather than a inscribed circle (ellipsoid)
 * @return CVAPI(void)
 */
CVAPI(void) cvPutImageROI( const IplImage* src,
                           IplImage* dst,
                           CvRect32f rect32f CV_DEFAULT(cvRect32f(0,0,1,1,0)),
                           CvPoint2D32f shear CV_DEFAULT(cvPoint2D32f(0,0)),
                           const IplImage* mask CV_DEFAULT(NULL),
                           bool circumscribe CV_DEFAULT(0) )
{
    CvRect rect;
    float tx, ty, sx, sy, angle;
    IplImage* _src = NULL;
    IplImage* _mask = NULL;
    CV_FUNCNAME( "cvPutImageROI" );
    __CV_BEGIN__;
    rect = cvRectFromRect32f( rect32f );
    angle = rect32f.angle;

    CV_ASSERT( rect.width > 0 && rect.height > 0 );
    CV_ASSERT( src->depth == dst->depth );
    CV_ASSERT( src->nChannels == dst->nChannels );
    if( mask != NULL )
        CV_ASSERT( src->width == mask->width && src->height == mask->height );

    if( circumscribe )
    {
        CvBox32f box32f = cvBox32fFromRect32f( rect32f );
        // width and height from center (inscribed ellipsoid's a and b parameters)
        float a = box32f.width / 2.0;
        float b = box32f.height / 2.0;
        // diagonal distance to the corner of rectangle from center
        float d = sqrt( a * a + b * b );
        // get distance to the intersectional point of inscribed ellipsoid 
        // line with rectangle's diagonal line from center
        float cost = a / d;
        float sint = b / d;
        float c = sqrt( pow( a * cost, 2 ) + pow( b * sint, 2 ) );
        // this ratio enables to make circumscribed ellipsoid
        float ratio = d / c;
        box32f.width *= ratio;
        box32f.height *= ratio;
        // ellipsoidal paramter to rectangle parameter
        rect32f = cvRect32fFromBox32f( box32f );
        rect = cvRectFromRect32f( rect32f );
    }

    _src = (IplImage*)src;
    _mask = (IplImage*)mask;
    if( rect.width != src->width && rect.height != src->height )
    {
        _src = cvCreateImage( cvSize( rect.width, rect.height ), src->depth, src->nChannels );
        cvResize( src, _src );
        if( mask != NULL )
        {
            _mask = cvCreateImage( cvSize( rect.width, rect.height ), mask->depth, mask->nChannels );
            cvResize( mask, _mask );
        }
    }

    if( angle == 0 && shear.x == 0 && shear.y == 0 && 
        rect.x >= 0 && rect.y >= 0 && 
        rect.x + rect.width < dst->width && rect.y + rect.height < dst->height )
    {
        cvSetImageROI( dst, rect );
        cvCopy( _src, dst, _mask );
        cvResetImageROI( dst );
    }
    else
    {
        if( _mask == NULL )
        {
            _mask = cvCreateImage( cvGetSize(_src), IPL_DEPTH_8U, 1 );
            cvSet( _mask, cvScalar(1) );
        }

        CvMat* affine = cvCreateMat( 2, 3, CV_32FC1 );
        tx = 0;
        ty = 0;
        sx = rect32f.width / (float)_src->width;
        sy = rect32f.height / (float)_src->height;
        angle = rect32f.angle;
        cvCreateAffine( affine, cvRect32f( tx, ty, sx, sy, angle ), shear );
        
        CvPoint origin;
        IplImage* srctrans = cvCreateAffineImage( _src, affine, CV_AFFINE_FULL, &origin, CV_RGB(0,0,0) );
        IplImage* masktrans  = cvCreateAffineImage( _mask, affine, CV_AFFINE_FULL, NULL, cvScalar(0) );
        for( int xp = 0; xp < srctrans->width; xp++ )
        {
            int x = xp + rect.x + origin.x;
            for( int yp = 0; yp < srctrans->height; yp++ )
            {
                int y = yp + rect.y + origin.y;
                if( x < 0 || x >= dst->width || y < 0 || y >= dst->height ) continue;
                if( CV_IMAGE_ELEM( masktrans, uchar, yp, xp ) == 0 ) continue;
                for( int ch = 0; ch < srctrans->nChannels; ch++ )
                {
                    dst->imageData[dst->widthStep * y + x * dst->nChannels + ch]
                        = srctrans->imageData[srctrans->widthStep * yp + xp * srctrans->nChannels + ch];
                }
            }
        }
        cvReleaseMat( &affine );
        cvReleaseImage( &srctrans );
        cvReleaseImage( &masktrans );
    }
    if( mask != _mask )
        cvReleaseImage( &_mask );
    if( src != _src )
        cvReleaseImage( &_src );
    __CV_END__;
}

#endif
