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

OpenCVを利用した, ビューポートのキャプチャ機能

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


#pragma once

#include <cassert>

#include "../../graph/OpenCVImage.h"



namespace lib_gl
{


//! カレントビューポートのレンダリング結果を画像化する
bool CvCaptureView( lib_graph::CvImage& o_image , bool i_autoFilpY = true )
{
	GLint viewport[4];
	glGetIntegerv( GL_VIEWPORT , viewport );
	GLint x = viewport[0];
	GLint y = viewport[1];
	GLint w = viewport[2];
	GLint h = viewport[3];

	o_image.CreateImage( (int)w , (int)h );
	glReadBuffer( GL_FRONT );
	glPixelStorei( GL_UNPACK_ALIGNMENT , o_image.GetImage()->align );
	glReadPixels( x , y , w , h , GL_BGR , GL_UNSIGNED_BYTE , o_image.GetImage()->imageData );

	if( i_autoFilpY )
		o_image.Filp_Y();

	return true;
}

bool CvCaptureView( const char* i_filename , bool i_autoFilpY = true )
{
	lib_graph::CvImage img;
	if( !CvCaptureView( img , i_autoFilpY ) )
		return false;

	if( !img.SaveImageFile( i_filename ) )
		return false;

	return true;
}


//! 動画キャプチャ機能
class CvVideoCapture
{
public:
	CvVideoCapture(void)
		: m_VideoWriter(NULL)
		, m_FrameImage(NULL)
		, m_VideoWidth(0)
		, m_VideoHeight(0)
	{}

	virtual ~CvVideoCapture(void)
	{
		if( CaptureRunning() )
			CloseCapture();
	}

	bool BeginCapture( const char* filename , int fourcc , double fps );
	bool BeginCapture( const char* filename , int fourcc , double fps , int width , int height );

	bool WriteCurrentFrame(void);

	bool CloseCapture(void);

	bool CaptureRunning(void) const
	{
		return ( m_VideoWriter != NULL );
	}


protected:
	CvVideoWriter* m_VideoWriter;
	IplImage*      m_FrameImage;
	int            m_VideoWidth;
	int            m_VideoHeight;
};



inline bool CvVideoCapture::BeginCapture( const char* filename , int fourcc , double fps )
{
	GLint viewport[4];
	glGetIntegerv( GL_VIEWPORT , viewport );
	int w = static_cast<int>( viewport[2] );
	int h = static_cast<int>( viewport[3] );

	// 幅が4の倍数でなければうまくいかないので補正する
	int w_mod = w % 4;
	if( w_mod != 0 )
	{
		w += 4 - w_mod;
	}

	return BeginCapture( filename , fourcc , fps , w , h );
}

inline bool CvVideoCapture::BeginCapture( const char* filename , int fourcc , double fps , int width , int height )
{
	assert( 0 <= width );
	assert( 0 <= height );
	assert( width % 4 == 0 );

	assert( m_VideoWriter == NULL );
	if( m_VideoWriter != NULL )
		return false;

	m_FrameImage = cvCreateImage( cvSize( width , height ) , IPL_DEPTH_8U , 3 );
	if( m_FrameImage == NULL )
		return false;

	m_VideoWriter = cvCreateVideoWriter( filename , fourcc , fps , cvSize( width , height ) );
	if( m_VideoWriter == NULL )
	{
		cvReleaseImage( &m_FrameImage );
		m_FrameImage = NULL;
		return false;
	}

	m_VideoWidth  = width;
	m_VideoHeight = height;

	return true;
}


inline bool CvVideoCapture::WriteCurrentFrame(void)
{
	GLint viewport[4];
	glGetIntegerv( GL_VIEWPORT , viewport );
	GLint x = viewport[0];
	GLint y = viewport[1];
	GLint w = viewport[2];
	GLint h = viewport[3];

	if( w > m_FrameImage->width  ) w = m_FrameImage->width;
	if( h > m_FrameImage->height ) h = m_FrameImage->height;

	// clear image
	cvSet( m_FrameImage , cvScalar( 0 , 0 , 0 ) );

	glPushAttrib( GL_PIXEL_MODE_BIT );

	glReadBuffer( GL_FRONT );
	glPixelStorei( GL_UNPACK_ALIGNMENT , m_FrameImage->align );
	glPixelStorei( GL_PACK_ROW_LENGTH , m_FrameImage->width );
	glReadPixels( x , y , w , h , GL_BGR , GL_UNSIGNED_BYTE , m_FrameImage->imageData );

	glPopAttrib();

	cvFlip( m_FrameImage , NULL , 0 );

	cvWriteFrame( m_VideoWriter , m_FrameImage );

	return true;
}


inline bool CvVideoCapture::CloseCapture(void)
{
	assert( m_VideoWriter != NULL );
	if( m_VideoWriter == NULL )
		return false;

	cvReleaseVideoWriter( &m_VideoWriter );
	m_VideoWriter = NULL;

	cvReleaseImage( &m_FrameImage );
	m_FrameImage = NULL;

	m_VideoWidth  = 0;
	m_VideoHeight = 0;

	return true;
}

}
