首页 > 代码库 > OpenCV:基于MFC的视频播放器和图片读取器

OpenCV:基于MFC的视频播放器和图片读取器

实例工程包下载【OpenCV:基于MFC的视频播放器和图片读取器】

一、实现的功能

1、打开本地视频进行读取、播放、暂停、停止控制

2、图片打开功能分为两种:可使用“打开图片”按钮打开本地图片,或者点击comobox里面设定的图片列表选择并显示图片

二、编译环境

OS:Win8.1 x64

IDE:  Visual Studio 2013

OpenCV: 2.4.8

三、主要思路

1、视频的滑动条控制:

  ①主窗体的OnHScroll()响应函数负责响应slider滑动条的变化,这样就轻松搞定slider控件与拖动滑动条动作与视频播放的联动。

  ②新建虚拟的slider实例和slider滑动位置的全局变量,通过数据交换函数予以关系绑定

  ③通过OpenCV的函数cvSetCaptureProperty()和slider滑动位置全局变量来确定指向视频的哪个帧位置,并进行加载显示到Picture控件中

2、combobox中图片的对应显示:

   ①addstring()向列表中添加选项

   ②将图片名字改为列表中的选项名

   ③根据选项名通过cvloadImage()打开图片

四、核心代码

<span style="font-family:Microsoft YaHei;">// VideoPlayerDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "VideoPlayer.h"
#include "VideoPlayerDlg.h"
#include "CvvImage.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// 对话框数据
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CVideoPlayerDlg 对话框




CVideoPlayerDlg::CVideoPlayerDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CVideoPlayerDlg::IDD, pParent)
	, IsPlaying(false)
	, IsAviFile(false)
	, g_VideoPath(_T(""))
	, pDC(NULL)
	, pwnd(NULL)
	, b_flagProcess(false)
	, g_m_int(0)
	, g_pCapture(NULL)
	, g_pFrame(NULL)
	, g_m_run(0)
	, TheImage(NULL)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CVideoPlayerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_SLIDER1, m_slider);
	DDX_Control(pDX, IDC_COMBO4, m_cbExample);
}

BEGIN_MESSAGE_MAP(CVideoPlayerDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_Open, &CVideoPlayerDlg::OnBnClickedOpen)
	ON_BN_CLICKED(IDC_Stop, &CVideoPlayerDlg::OnBnClickedStop)
	ON_WM_HSCROLL()
	ON_BN_CLICKED(IDC_Play, &CVideoPlayerDlg::OnBnClickedPlay)
	ON_BN_CLICKED(IDOK, &CVideoPlayerDlg::OnBnClickedOk)
	ON_BN_CLICKED(IDCANCEL, &CVideoPlayerDlg::OnBnClickedCancel)
	ON_BN_CLICKED(IDC_Pause, &CVideoPlayerDlg::OnBnClickedPause)
	ON_STN_CLICKED(IDC_SHOWPIC, &CVideoPlayerDlg::OnStnClickedShowpic)
	ON_BN_CLICKED(IDC_img_open, &CVideoPlayerDlg::OnBnClickedReadimg)
	ON_EN_CHANGE(IDC_EDIT_FRAME_show, &CVideoPlayerDlg::OnEnChangeEditFrameshow)
	ON_CBN_SELCHANGE(IDC_COMBO4, &CVideoPlayerDlg::OnCbnSelchangeCombo4)
END_MESSAGE_MAP()


// CVideoPlayerDlg 消息处理程序

BOOL CVideoPlayerDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	// 将“关于...”菜单项添加到系统菜单中。
	// IDM_ABOUTBOX 必须在系统命令范围内。
	CvSize ImgSize;
	ImgSize.height = IMAGE_HEIGHT;
	ImgSize.width = IMAGE_WIDTH;
	TheImage = cvCreateImage(ImgSize, IPL_DEPTH_8U, IMAGE_CHANNELS);
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	GetDlgItem( IDC_Play )->EnableWindow( FALSE );
	GetDlgItem( IDC_Pause )->EnableWindow( FALSE );
	GetDlgItem( IDC_Stop )->EnableWindow( FALSE );
	GetDlgItem( IDC_SLIDER1 )->EnableWindow( FALSE );	

	m_cbExample.AddString("photos/1.bmp");       //combobox里面的图片保存在photos文件夹内
	m_cbExample.AddString("photos/2.bmp");
	m_cbExample.AddString("photos/3.bmp");
	m_cbExample.AddString("photos/4.bmp");
	m_cbExample.AddString("photos/5.bmp");
	m_cbExample.AddString("photos/6.bmp");
	m_cbExample.AddString("photos/7.bmp");
	m_cbExample.AddString("photos/8.bmp");
	m_cbExample.AddString("photos/9.bmp");
	m_cbExample.AddString("photos/10.bmp");
	pwnd = GetDlgItem(IDC_SHOWPIC);
	pwnd->MoveWindow(40,40,352,288);
    pDC =pwnd->GetDC();
    hDC= pDC->GetSafeHdc();
	pwnd->GetClientRect(&rect);
	Invalidate();
	m_slider.SetRange(0,100);
	m_slider.SetTicFreq(10);
	ImgSize.height = IMAGE_HEIGHT;
	ImgSize.width = IMAGE_WIDTH;
	g_pFrame = cvCreateImage( ImgSize, IPL_DEPTH_8U, IMAGE_CHANNELS );

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CVideoPlayerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CVideoPlayerDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
		CDialog::UpdateWindow();                // 更新windows窗口,如果无这步调用,图片显示还会出现问题
		ShowImage(TheImage, IDC_ShowImg);    // 重绘图片函数
	}
	if(!g_m_run)
	{
		CDC MemDC; 
		MemDC.CreateCompatibleDC(NULL);
		pDC->StretchBlt(rect.left,rect.top,rect.Width(),rect.Height(),&MemDC,0,0,48,48,SRCCOPY); 
		MemDC.DeleteDC();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CVideoPlayerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


void CVideoPlayerDlg::OnBnClickedOpen()
{
	// TODO: 在此添加控件通知处理程序代码
	CFileDialog dlg(
		TRUE,
		_T("*.avi"), 
		NULL,
		OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
		_T("*.avi|*.avi|*.rmvb|*.rmvb|*.mp4|*.mp4|*.wmv|*.wmv| All Files (*.*) |*.*||"), 
		NULL
		);	
	dlg.m_ofn.lpstrTitle = _T("选取视频文件");	
	if( dlg.DoModal() != IDOK )		
		return;

	g_VideoPath = dlg.GetPathName();

	GetDlgItem( IDC_Play )->EnableWindow( TRUE );
	GetDlgItem( IDC_Pause )->EnableWindow( TRUE );
	GetDlgItem( IDC_Stop )->EnableWindow( TRUE );
	GetDlgItem( IDC_SLIDER1 )->EnableWindow( TRUE );

}




void CVideoPlayerDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	CSliderCtrl   *pSlidCtrl=(CSliderCtrl*)GetDlgItem(IDC_SLIDER1);
	g_m_int=pSlidCtrl->GetPos();
	char chEdit[10];
	itoa(g_m_int,chEdit,10);
	SetDlgItemText(IDC_EDIT_FRAME_show,chEdit);
	if(g_pCapture)
	{
		cvSetCaptureProperty(g_pCapture,CV_CAP_PROP_POS_FRAMES,g_m_int);
	}
	CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CVideoPlayerDlg::OnBnClickedPlay()
{
	// TODO: 在此添加控件通知处理程序代码
	if( g_VideoPath == "" ) 
	{
		MessageBox("请先选择视频文件!");
		return;
	}
	else
	{
		if(!(g_pCapture = cvCreateFileCapture( g_VideoPath )))
		{
			MessageBox("打开视频文件失败!");
			return;
		}
	}
	int frames = (int) cvGetCaptureProperty( g_pCapture, CV_CAP_PROP_FRAME_COUNT );
	m_slider.SetRange(0,frames-1);
	m_slider.SetTicFreq(1);
	char chEdit[10];
	itoa(frames,chEdit,10);
	SetDlgItemText(IDC_EDIT_FRAME_count,chEdit);
	GetDlgItem( IDC_Open )->EnableWindow( FALSE );
	GetDlgItem( IDC_Play )->EnableWindow( FALSE );
	GetDlgItem( IDC_Pause )->EnableWindow( TRUE );
	GetDlgItem( IDC_Stop )->EnableWindow( TRUE );
	IplImage *pFrame = NULL;
	cvNamedWindow("video");
	cvResizeWindow("video",1,1);
	HWND hWnd = (HWND) cvGetWindowHandle("video");
	HWND hParent = ::GetParent(hWnd);
	HWND hwnd1=::FindWindow("CVideoPlayerDlg","VideoPlayer");
	::SetParent(hWnd, hwnd1);
	::ShowWindow(hParent, SW_HIDE);
	if( g_pFrame )								
		cvZero( g_pFrame );
	g_m_run = 1;
	if(g_pCapture)
	{
		cvSetCaptureProperty(g_pCapture,CV_CAP_PROP_POS_FRAMES,g_m_int);
	 }
	while( (g_m_int < frames) & (g_m_run == 1) )
    { 
		pFrame = cvQueryFrame( g_pCapture );
		IplImage* newframe = cvCloneImage(pFrame);
		ResizeImage( newframe );
		ShowImage( g_pFrame , IDC_SHOWPIC);
		if (cvWaitKey(20) == 27) 
			break;
		m_slider.SetPos(g_m_int);
		char chEdit[10];
		//g_m_int=numfrm;
		itoa(g_m_int,chEdit,10);
		SetDlgItemText(IDC_EDIT_FRAME_show,chEdit);

		//numfrm++;
		g_m_int++;
		cvReleaseImage( &newframe );
	}
	if(g_m_int==frames)
	{
		g_m_int=0;
		m_slider.SetPos(g_m_int);
		g_m_run = 0;
		if( g_pFrame )	
		{
			cvZero( g_pFrame  );
			ShowImage( g_pFrame , IDC_SHOWPIC );
		}
		GetDlgItem( IDC_Open )->EnableWindow( TRUE );
		GetDlgItem( IDC_Play )->EnableWindow( TRUE );
		GetDlgItem( IDC_Pause )->EnableWindow( FALSE );
		GetDlgItem( IDC_Stop )->EnableWindow( FALSE );
	}	
	
    cvReleaseCapture(&g_pCapture);
	cvDestroyWindow("video");
}

void CVideoPlayerDlg::OnBnClickedStop()
{
	// TODO: 在此添加控件通知处理程序代码
	
	CDC MemDC; 
	MemDC.CreateCompatibleDC(NULL);
	pDC->StretchBlt(rect.left,rect.top,rect.Width(),rect.Height(),&MemDC,0,0,48,48,SRCCOPY); 
	MemDC.DeleteDC();
	GetDlgItem( IDC_Open )->EnableWindow( TRUE );
	GetDlgItem( IDC_Play )->EnableWindow( TRUE );
	GetDlgItem( IDC_Pause )->EnableWindow( FALSE );
	GetDlgItem( IDC_Stop )->EnableWindow( FALSE );
	g_m_run=0;
	g_m_int=0;
	m_slider.SetPos(g_m_int);
	char chEdit[10];
	itoa(g_m_int,chEdit,10);
	SetDlgItemText(IDC_EDIT_FRAME_show,chEdit);

	return;

}
void CVideoPlayerDlg::ResizeImage(IplImage* img)
{
    int w = img->width;
	int h = img->height;
	int max = (w > h)? w: h;
	float scale = (float) ( (float) max / 256.0f );
	int nw = (int)( w/scale );
	int nh = (int)( h/scale );
	int tlx = (nw > nh)? 0: (int)(256-nw)/2;
	int tly = (nw > nh)? (int)(256-nh)/2: 0;
	cvSetImageROI( g_pFrame, cvRect( tlx, tly, nw, nh) );
	cvResize( img, g_pFrame );
	cvResetImageROI( g_pFrame );
}
void CVideoPlayerDlg::ShowImage(IplImage* img, UINT ID)
{
	CDC* pDC = GetDlgItem( ID ) ->GetDC();		
	HDC hDC = pDC ->GetSafeHdc();	
	CRect rect;                                
	GetDlgItem(ID) ->GetClientRect( &rect );   
	int rw = rect.right - rect.left;
	int rh = rect.bottom - rect.top;
	int iw = img->width;
	int ih = img->height;
	int tx = (int)(rw - iw)/2;	
	int ty = (int)(rh - ih)/2;
	SetRect( rect, tx, ty, tx+iw, ty+ih );
	CvvImage cimg;
	cimg.CopyOf( img );				
	cimg.DrawToHDC( hDC, &rect );
	ReleaseDC( pDC );
}

void CVideoPlayerDlg::OnBnClickedOk()
{
	// TODO: 在此添加控件通知处理程序代码
	g_m_run = 0;
	cvReleaseImage( &g_pFrame );
	cvDestroyAllWindows();
	OnOK();
}

void CVideoPlayerDlg::OnBnClickedCancel()
{
	// TODO: 在此添加控件通知处理程序代码
	g_m_run = 0;
	cvReleaseImage( &g_pFrame );
	cvDestroyAllWindows();
	OnCancel();
}

void CVideoPlayerDlg::OnBnClickedPause()
{
	// TODO: 在此添加控件通知处理程序代码
	g_m_run=0;
	GetDlgItem( IDC_Open )->EnableWindow( FALSE );
	GetDlgItem( IDC_Play )->EnableWindow( TRUE );
	GetDlgItem( IDC_Pause )->EnableWindow( FALSE );
	GetDlgItem( IDC_Stop )->EnableWindow( FALSE );
}


void CVideoPlayerDlg::OnStnClickedShowpic()
{
	// TODO:  在此添加控件通知处理程序代码
}

void CVideoPlayerDlg::OnBnClickedReadimg()
{
	// TODO:  在此添加控件通知处理程序代码
	CFileDialog dlg(
		TRUE, _T("*.bmp"), NULL,
		OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
		_T("image files (*.bmp; *.jpg) |*.bmp; *.jpg | All Files (*.*) |*.*||"), NULL
		);                                        // 选项图片的约定
	dlg.m_ofn.lpstrTitle = _T("Open Image");    // 打开文件对话框的标题名
	if (dlg.DoModal() != IDOK)                    // 判断是否获得图片
		return;

	CString mPath = dlg.GetPathName();            // 获取图片路径

	IplImage* ipl = cvLoadImage(mPath, 1);    // 读取图片、缓存到一个局部变量 ipl 中
	if (!ipl)                                    // 判断是否成功载入图片
		return;
	if (TheImage)                                // 对上一幅显示的图片数据清零
		cvZero(TheImage);

	resize_image(ipl);    // 对读入的图片进行缩放,使其宽或高最大值者刚好等于 256,再复制到 TheImage 中
	show_image(TheImage, IDC_ShowImg);            // 调用显示图片函数    
	cvReleaseImage(&ipl);                        // 释放 ipl 占用的内存
}

void CVideoPlayerDlg::show_image(IplImage* img, UINT ID)
{
	CDC* pDC = GetDlgItem(ID)->GetDC();        // 获得显示控件的 DC
	HDC hDC = pDC->GetSafeHdc();                // 获取 HDC(设备句柄) 来进行绘图操作

	CRect rect;
	GetDlgItem(ID)->GetClientRect(&rect);
	int rw = rect.right - rect.left;            // 求出图片控件的宽和高
	int rh = rect.bottom - rect.top;
	int iw = img->width;                        // 读取图片的宽和高
	int ih = img->height;
	int tx = (int)(rw - iw) / 2;                    // 使图片的显示位置正好在控件的正中
	int ty = (int)(rh - ih) / 2;
	SetRect(rect, tx, ty, tx + iw, ty + ih);

	CvvImage cimg;
	cimg.CopyOf(img);                            // 复制图片
	cimg.DrawToHDC(hDC, &rect);                // 将图片绘制到显示控件的指定区域内

	ReleaseDC(pDC);
}


void CVideoPlayerDlg::resize_image(IplImage* img)
{
	// 读取图片的宽和高
	int w = img->width;
	int h = img->height;

	// 找出宽和高中的较大值者
	int max = (w > h) ? w : h;

	// 计算将图片缩放到TheImage区域所需的比例因子
	float scale = (float)((float)max / 256.0f);

	// 缩放后图片的宽和高
	int nw = (int)(w / scale);
	int nh = (int)(h / scale);

	// 为了将缩放后的图片存入 TheImage 的正中部位,需计算图片在 TheImage 左上角的期望坐标值
	int tlx = (nw > nh) ? 0 : (int)(256 - nw) / 2;
	int tly = (nw > nh) ? (int)(256 - nh) / 2 : 0;

	// 设置 TheImage 的 ROI 区域,用来存入图片 img
	cvSetImageROI(TheImage, cvRect(tlx, tly, nw, nh));

	// 对图片 img 进行缩放,并存入到 TheImage 中
	cvResize(img, TheImage);

	// 重置 TheImage 的 ROI 准备读入下一幅图片
	cvResetImageROI(TheImage);
}


void CVideoPlayerDlg::OnEnChangeEditFrameshow()
{
	// TODO:  如果该控件是 RICHEDIT 控件,它将不
	// 发送此通知,除非重写 CDialog::OnInitDialog()
	// 函数并调用 CRichEditCtrl().SetEventMask(),
	// 同时将 ENM_CHANGE 标志“或”运算到掩码中。

	// TODO:  在此添加控件通知处理程序代码
}




void CVideoPlayerDlg::OnCbnSelchangeCombo4()
{
	int nIndex = m_cbExample.GetCurSel();
	CString strCBText;
	m_cbExample.GetLBText(nIndex, strCBText);
	IplImage* ipl = cvLoadImage(strCBText, 1);    // 读取图片、缓存到一个局部变量 ipl 中
	if (!ipl)                                    // 判断是否成功载入图片
		return;
	if (TheImage)                                // 对上一幅显示的图片数据清零
		cvZero(TheImage);

	resize_image(ipl);    // 对读入的图片进行缩放,使其宽或高最大值者刚好等于 256,再复制到 TheImage 中
	show_image(TheImage, IDC_ShowImg);            // 调用显示图片函数    
	cvReleaseImage(&ipl);                        // 释放 ipl 占用的内存
	// TODO:  在此添加控件通知处理程序代码


}


</span>
五、相关问题的解决

1、OpenCV2.4.8找不到CImage(CvvImage)的解决办法:为了不和MFC中的CImage冲突,我们重新将CvvImage定义为CvImage。
在需要的地方引入 #include "CvvImage.h" 就可以用了。

<span style="font-family:Microsoft YaHei;">为了不和MFC中的CImage冲突,我们重新将CvvImage定义为CvImage。
在需要的地方引入 #include "CvvImage.h" 就可以用了。
//================================================================
// CvvImage.h
//================================================================
#pragma once

#ifndef CVVIMAGE_CLASS_DEF
#define CVVIMAGE_CLASS_DEF

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgproc/imgproc_c.h"


class CvvImage
{
public:
CvvImage();
virtual ~CvvImage();


virtual bool Create( int width, int height, int bits_per_pixel, int image_origin = 0 );


virtual bool Load( const char* filename, int desired_color = 1 );


virtual bool LoadRect( const char* filename,
int desired_color, CvRect r );

#if defined WIN32 || defined _WIN32
virtual bool LoadRect( const char* filename,
int desired_color, RECT r )
{
return LoadRect( filename, desired_color,
cvRect( r.left, r.top, r.right - r.left, r.bottom - r.top ));
}
#endif


virtual bool Save( const char* filename );


virtual void CopyOf( CvvImage& image, int desired_color = -1 );
virtual void CopyOf( IplImage* img, int desired_color = -1 );

IplImage* GetImage() { return m_img; };
virtual void Destroy(void);


int Width() { return !m_img ? 0 : !m_img->roi ? m_img->width : m_img->roi->width; };
int Height() { return !m_img ? 0 : !m_img->roi ? m_img->height : m_img->roi->height;};
int Bpp() { return m_img ? (m_img->depth & 255)*m_img->nChannels : 0; };

virtual void Fill( int color );


virtual void Show( const char* window );

#if defined WIN32 || defined _WIN32

virtual void Show( HDC dc, int x, int y, int width, int height,
int from_x = 0, int from_y = 0 );

virtual void DrawToHDC( HDC hDCDst, RECT* pDstRect );
#endif

protected:

IplImage* m_img;
};

typedef CvvImage CvImage;

#endif

//================================================================
// CvvImage.cpp
//================================================================

#include "StdAfx.h"
#include "CvvImage.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CV_INLINE RECT NormalizeRect( RECT r );
CV_INLINE RECT NormalizeRect( RECT r )
{
int t;

if( r.left > r.right )
{
t = r.left;
r.left = r.right;
r.right = t;
}

if( r.top > r.bottom )
{
t = r.top;
r.top = r.bottom;
r.bottom = t;
}

return r;
}

CV_INLINE CvRect RectToCvRect( RECT sr );
CV_INLINE CvRect RectToCvRect( RECT sr )
{
sr = NormalizeRect( sr );
return cvRect( sr.left, sr.top, sr.right - sr.left, sr.bottom - sr.top );
}

CV_INLINE RECT CvRectToRect( CvRect sr );
CV_INLINE RECT CvRectToRect( CvRect sr )
{
RECT dr;
dr.left = sr.x;
dr.top = sr.y;
dr.right = sr.x + sr.width;
dr.bottom = sr.y + sr.height;

return dr;
}

CV_INLINE IplROI RectToROI( RECT r );
CV_INLINE IplROI RectToROI( RECT r )
{
IplROI roi;
r = NormalizeRect( r );
roi.xOffset = r.left;
roi.yOffset = r.top;
roi.width = r.right - r.left;
roi.height = r.bottom - r.top;
roi.coi = 0;

return roi;
}

void FillBitmapInfo( BITMAPINFO* bmi, int width, int height, int bpp, int origin )
{
assert( bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32));

BITMAPINFOHEADER* bmih = &(bmi->bmiHeader);

memset( bmih, 0, sizeof(*bmih));
bmih->biSize = sizeof(BITMAPINFOHEADER);
bmih->biWidth = width;
bmih->biHeight = origin ? abs(height) : -abs(height);
bmih->biPlanes = 1;
bmih->biBitCount = (unsigned short)bpp;
bmih->biCompression = BI_RGB;

if( bpp == 8 )
{
RGBQUAD* palette = bmi->bmiColors;
int i;
for( i = 0; i < 256; i++ )
{
palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;
palette[i].rgbReserved = 0;
}
}
}

CvvImage::CvvImage()
{
m_img = 0;
}

void CvvImage::Destroy()
{
cvReleaseImage( &m_img );
}

CvvImage::~CvvImage()
{
Destroy();
}

bool CvvImage::Create( int w, int h, int bpp, int origin )
{
const unsigned max_img_size = 10000;

if( (bpp != 8 && bpp != 24 && bpp != 32) ||
(unsigned)w >= max_img_size || (unsigned)h >= max_img_size ||
(origin != IPL_ORIGIN_TL && origin != IPL_ORIGIN_BL))
{
assert(0); // most probably, it is a programming error
return false;
}

if( !m_img || Bpp() != bpp || m_img->width != w || m_img->height != h )
{
if( m_img && m_img->nSize == sizeof(IplImage))
Destroy();


m_img = cvCreateImage( cvSize( w, h ), IPL_DEPTH_8U, bpp/8 );
}

if( m_img )
m_img->origin = origin == 0 ? IPL_ORIGIN_TL : IPL_ORIGIN_BL;

return m_img != 0;
}

void CvvImage::CopyOf( CvvImage& image, int desired_color )
{
IplImage* img = image.GetImage();
if( img )
{
CopyOf( img, desired_color );
}
}


#define HG_IS_IMAGE(img) ((img) != 0 && ((const IplImage*)(img))->nSize == sizeof(IplImage) && ((IplImage*)img)->imageData != 0)


void CvvImage::CopyOf( IplImage* img, int desired_color )
{
if( HG_IS_IMAGE(img) )
{
int color = desired_color;
CvSize size = cvGetSize( img );

if( color < 0 )
color = img->nChannels > 1;

if( Create( size.width, size.height,
(!color ? 1 : img->nChannels > 1 ? img->nChannels : 3)*8,
img->origin ))
{
cvConvertImage( img, m_img, 0 );
}
}
}


bool CvvImage::Load( const char* filename, int desired_color )
{
IplImage* img = cvLoadImage( filename, desired_color );
if( !img )
return false;

CopyOf( img, desired_color );
cvReleaseImage( &img );

return true;
}


bool CvvImage::LoadRect( const char* filename,
int desired_color, CvRect r )
{
if( r.width < 0 || r.height < 0 ) return false;

IplImage* img = cvLoadImage( filename, desired_color );
if( !img )
return false;

if( r.width == 0 || r.height == 0 )
{
r.width = img->width;
r.height = img->height;
r.x = r.y = 0;
}

if( r.x > img->width || r.y > img->height ||
r.x + r.width < 0 || r.y + r.height < 0 )
{
cvReleaseImage( &img );
return false;
}


if( r.x < 0 )
{
r.width += r.x;
r.x = 0;
}
if( r.y < 0 )
{
r.height += r.y;
r.y = 0;
}

if( r.x + r.width > img->width )
r.width = img->width - r.x;

if( r.y + r.height > img->height )
r.height = img->height - r.y;

cvSetImageROI( img, r );
CopyOf( img, desired_color );

cvReleaseImage( &img );
return true;
}


bool CvvImage::Save( const char* filename )
{
if( !m_img )
return false;
cvSaveImage( filename, m_img );
return true;
}


void CvvImage::Show( const char* window )
{
if( m_img )
cvShowImage( window, m_img );
}


void CvvImage::Show( HDC dc, int x, int y, int w, int h, int from_x, int from_y )
{
if( m_img && m_img->depth == IPL_DEPTH_8U )
{
uchar buffer[sizeof(BITMAPINFOHEADER) + 1024];
BITMAPINFO* bmi = (BITMAPINFO*)buffer;
int bmp_w = m_img->width, bmp_h = m_img->height;

FillBitmapInfo( bmi, bmp_w, bmp_h, Bpp(), m_img->origin );

from_x = MIN( MAX( from_x, 0 ), bmp_w - 1 );
from_y = MIN( MAX( from_y, 0 ), bmp_h - 1 );

int sw = MAX( MIN( bmp_w - from_x, w ), 0 );
int sh = MAX( MIN( bmp_h - from_y, h ), 0 );

SetDIBitsToDevice(
dc, x, y, sw, sh, from_x, from_y, from_y, sh,
m_img->imageData + from_y*m_img->widthStep,
bmi, DIB_RGB_COLORS );
}
}


void CvvImage::DrawToHDC( HDC hDCDst, RECT* pDstRect )
{
if( pDstRect && m_img && m_img->depth == IPL_DEPTH_8U && m_img->imageData )
{
uchar buffer[sizeof(BITMAPINFOHEADER) + 1024];
BITMAPINFO* bmi = (BITMAPINFO*)buffer;
int bmp_w = m_img->width, bmp_h = m_img->height;

CvRect roi = cvGetImageROI( m_img );
CvRect dst = RectToCvRect( *pDstRect );

if( roi.width == dst.width && roi.height == dst.height )
{
Show( hDCDst, dst.x, dst.y, dst.width, dst.height, roi.x, roi.y );
return;
}

if( roi.width > dst.width )
{
SetStretchBltMode(
hDCDst, // handle to device context
HALFTONE );
}
else
{
SetStretchBltMode(
hDCDst, // handle to device context
COLORONCOLOR );
}

FillBitmapInfo( bmi, bmp_w, bmp_h, Bpp(), m_img->origin );

::StretchDIBits(
hDCDst,
dst.x, dst.y, dst.width, dst.height,
roi.x, roi.y, roi.width, roi.height,
m_img->imageData, bmi, DIB_RGB_COLORS, SRCCOPY );
}
}


void CvvImage::Fill( int color )
{
cvSet( m_img, cvScalar(color&255,(color>>8)&255,(color>>16)&255,(color>>24)&255) );
}</span>