ddraw 视频下画图 不闪烁的方法

      我们如果是在在RGB视频上画图(直线,矩形等),一般采用双缓冲区继续,使用内存MemoryDC,来实现画的图形在视频上显示不闪烁的功能,但是我们知道用RGB显示视频都是使用GDI进行渲染,这样很耗CPU,那么我们能不能在YUV上进行视频渲染呢,答案是肯定的,使用ddraw直接显示yuv就ok了,可以支持yuv422和yuv420的直接使用显卡显示,不耗CPU,但是我们在使用ddraw显示时,然后在配合GDI画图(直线或矩形等),画的图形是闪烁的,原因是我们在ddraw直接显示yuv视频时,使用的是离屏表面的方法,将yuv数据拷贝到离屏表面,然后在blt到主表面,这样用gdi画图时,和视频刷新不同步,造成闪烁,那么我们怎么解决该问题呢?方法如下:

新增加一个离屏表面,我们定义成osd离屏表面吧,我们将yuv数据拷贝到离屏表面后,在将该离屏表面blt到osd离屏表面,然后在osd离屏表面上画直线或矩形,画完后在blt到主表面,这样画的图形就不会闪烁了。

 

直接上源码吧,注意,我没有对画图的部分进行封装,感兴趣的朋友可以自己封装;

#ifndef	_DIRECTDRAW_H_
#define	_DIRECTDRAW_H_
#pragma once
#include "ddraw.h"

#define   FOURCC_YUYV	0x32595559   //   MAKEFOURCC( 'Y ', 'U ', 'Y ', '2 ') 
#define   FOURCC_UYVY	0x59565955   //   MAKEFOURCC( 'U ', 'Y ', 'V ', 'Y ') 

#define  YUV_UYVY 1
#define  YUV_YUYV 2

class __declspec(dllexport) CDirectDraw
{
public:
	CDirectDraw(void);
	~CDirectDraw(void);

	bool DirectDrawInit(HWND hWnd, int width, int height,DWORD dwYuvFourCC);
	bool DisPlayYUVData(byte *pYUVData,int bYuvType,RECT rect);				
	void DirectDrawDeInit(void);										
protected:
	LPDIRECTDRAW7           lpDD;											// DirectDraw 对象指针
	LPDIRECTDRAWSURFACE7    lpDDSPrimary;	// DirectDraw 主表面指针
	LPDIRECTDRAWSURFACE7    lpDDSOffScr;		// DirectDraw 离屏表面指针
	DDSURFACEDESC2			ddsd;	// DirectDraw 表面描述

	LPDIRECTDRAWSURFACE7 m_pOsdSurface;		//画图表面

};

#endif


DirectDraw.cpp 源文件如下:

#include "StdAfx.h"
#include "DirectDraw.h"
#include <string>
using namespace std;

CDirectDraw::CDirectDraw(void)
{

	lpDD=NULL;			  // DirectDraw 对象指针
	lpDDSPrimary=NULL;   // DirectDraw 主表面指针
	lpDDSOffScr=NULL;   // DirectDraw 离屏表面指针
	m_pOsdSurface=NULL;

}

CDirectDraw::~CDirectDraw(void)
{

	DirectDrawDeInit();
}

//yuv_type控件不同的yuv格式 
bool CDirectDraw::DirectDrawInit(HWND hWnd, int width, int height,DWORD dwYuvFourCC)
{
	
	HRESULT hr;
	// 创建DirectCraw对象
	if (DirectDrawCreateEx(NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL) != DD_OK)
	{
		return false;
	}

	// 设置协作层
	if (lpDD->SetCooperativeLevel(hWnd,
		DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES) != DD_OK)
	{
		return false;
	}

	// 创建主表面
	LPVOID  lpSurface  = NULL;
	ZeroMemory(&ddsd, sizeof(ddsd));
	ZeroMemory(&ddsd.ddpfPixelFormat, sizeof(DDPIXELFORMAT));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

	if (lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL) != DD_OK)
	{
		return false;
	}

	LPDIRECTDRAWCLIPPER   pcClipper;    // Cliper
	if( lpDD->CreateClipper( 0, &pcClipper, NULL ) != DD_OK )
		return false;

	if( pcClipper->SetHWnd( 0, hWnd ) != DD_OK )
	{
		pcClipper->Release();
		return false;
	}

	if( lpDDSPrimary->SetClipper( pcClipper ) != DD_OK )
	{
		pcClipper->Release();
		return false;
	}

	// Done with clipper
	pcClipper->Release();

	// 创建YUV表面
	ddsd.ddsCaps.dwCaps =  DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY ;
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT ;
	ddsd.dwWidth = width;	
	ddsd.dwHeight =height;	

	ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
	ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_YUV ;
	ddsd.ddpfPixelFormat.dwYUVBitCount = 8; 
	ddsd.ddpfPixelFormat.dwFourCC =dwYuvFourCC;

	hr=lpDD->CreateSurface(&ddsd, &lpDDSOffScr, NULL);

	if ( hr!= DD_OK)
	{
		return false;
	}

#if 1
	//创建OSD画图离屏表面
	//
	ZeroMemory(&ddsd, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	ddsd.dwWidth = width;
	ddsd.dwHeight = height;

	hr = lpDD->CreateSurface(&ddsd, &m_pOsdSurface, NULL);
	if ( hr != DD_OK)
	{
		//lpDD->Release();
		//lpDDSPrimary = NULL;
		//lpDD = NULL;
		return false;
	}

#endif


	return true;
}


//add rect参数,将图像缩放到rect内
bool CDirectDraw::DisPlayYUVData(byte *pYUVdata,int bYuvType,RECT rect)
{
	
	byte *pSurf;
	int yuv_type=bYuvType;

	HRESULT hr;
	hr=lpDDSOffScr->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT,NULL);

	//2012-02-24
	if (hr==DDERR_SURFACELOST)
	{
		TRACE("off surface lost,restore offscr\n");
		hr=lpDDSOffScr->Restore();
		hr=lpDDSOffScr->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT,NULL);
	}

	if (FAILED(hr))
	{
		return DD_FALSE;
	}

	//2012-02-11 
	if (yuv_type==YUV_UYVY)
	{
		ddsd.ddpfPixelFormat.dwFourCC =FOURCC_UYVY;
	}else
	{
		ddsd.ddpfPixelFormat.dwFourCC =FOURCC_YUYV;
	}
	
	pSurf=(LPBYTE)ddsd.lpSurface;
	if (pSurf)
	{
		for(unsigned int i=0; i < ddsd.dwHeight; i++)  
		{
			memcpy(pSurf,pYUVdata,ddsd.dwWidth*2);	
			pYUVdata+=ddsd.dwWidth*2;			
			pSurf+=ddsd.lPitch;
		}

	}


	lpDDSOffScr->Unlock(NULL); 

	
#if 1
	//加入Osd离屏表面内容
	HRESULT ddrval;
	ddrval = m_pOsdSurface->Blt(&rect, lpDDSOffScr, NULL, DDBLT_WAIT, NULL);
	if (ddrval != DD_OK)
	{
		ddrval = lpDDSPrimary->Blt(&rect, lpDDSOffScr, &rect, DDBLT_WAIT, NULL);
	}
	else
	{
		HDC hDC = NULL;
		ddrval = m_pOsdSurface->GetDC(&hDC);
		if ((ddrval == DD_OK)&&(hDC != NULL))
		{

			//叠加文字
			SetTextColor(hDC,RGB(255,0,0));
			SetBkColor(hDC,RGB(0,255,0));

			CString m_sOsdMsg=_T("hello world");
			TextOut(hDC, rect.left+100,rect.top+200 , m_sOsdMsg, m_sOsdMsg.GetLength());

			//画实心矩形
			HPEN hpen = CreatePen (PS_SOLID, 1, RGB(255, 0, 0));
			SelectObject (hDC, hpen);
			HBRUSH hbrush = CreateSolidBrush (RGB(0, 255, 0)); //创建刷子
			SelectObject (hDC, hbrush);						   //使用刷子


			Rectangle(hDC, rect.left+100, rect.top+100, rect.left+200, rect.top+200); //画矩形

			//画空心矩形
			RECT rect1;
			rect1.left=rect.left+200;
			rect1.top=rect.top+200;
			rect1.right=rect.left+300;
			rect1.bottom=rect.top+300;

			FrameRect(hDC,&rect1,CreateSolidBrush(RGB(255,0,0)));

			//画直线
			MoveToEx(hDC,rect.left+50,rect.top+50,NULL);
			LineTo(hDC,rect.left+350,rect.top+350);

			m_pOsdSurface->ReleaseDC(hDC);
			lpDDSPrimary->Blt(&rect, m_pOsdSurface, &rect, DDBLT_WAIT, NULL);
		}
	}

#else
	//只有主表面和离屏表面
	HRESULT ddrval;
	ddrval=lpDDSPrimary->Blt(&rect, lpDDSOffScr, NULL, DDBLT_WAIT, NULL);

	if (ddrval==DDERR_SURFACELOST)
	{
		TRACE("primary surface lost,restore all surfaces\n");
		lpDDSPrimary->Restore();
	}
#endif


	return DD_OK;

}

//ddraw deInit
void CDirectDraw::DirectDrawDeInit(void)
{
	if (lpDDSOffScr != NULL)
	{
		lpDDSOffScr->Release();
		lpDDSOffScr = NULL;
	}

	if (lpDDSPrimary != NULL)
	{
		lpDDSPrimary->Release();
		lpDDSPrimary = NULL;
	}

	if (lpDD != NULL)
	{
		lpDD->Release();
		lpDD = NULL;
	}

	if (m_pOsdSurface!=NULL)
	{
		m_pOsdSurface->Release();
		m_pOsdSurface=NULL;
	}
}


 

测试结果如下:

 

ddraw gdi 画图知识如下:

由于DirectDraw并没有提供画点、线,圆等的语句,所以我们要借助Windows GDI函数来完成这些工作。就像输出文字时一样,我们先要获得页面的HDC:

HDC hdc;

       lpDDSXXX->GetDC(&hdc);

       画点是最简单的,SetPixel (hdc, x, y, RGB(r, g, b));即可在屏幕的(x,y)坐标处画上一个指定颜色的点。

如果需要画线等,我们需要创建"画笔":

       HPEN hpen = CreatePen (PS_SOLID, 5, RGB(r, g, b));

       CreatePen的第一个参数意义为画笔样式,常用的有PS_SOLID(普通画笔)和PS_DOT(由间断点组成的画笔,需要设置画笔宽度为1)。第二个参数是画笔的宽度,第三个参数是画笔的颜色。

       接着将画笔给HDC:

       SelectObject (hdc, hpen);

移动画笔到(x1,y1):

       MoveToEx (hdc, x1, y1, NULL);

       从画图起始位置向(x2,y2)坐标处画线:

       LineTo (hdc, x2, y2);

       下面列出一些常用的画图语句,使用方法和画线差不多,设定完画笔即可使用:

       Rectangle(hdc, x1, y1, x2, y2); //画矩形

       Ellipse(hdc, x1, y1, x2, y2); //画椭圆

值得注意的是我们画的图形将由一个"刷子"来填充,使用最简单的单色刷子的方法是:

       HBRUSH hbrush = CreateSolidBrush (RGB(r, g, b));//创建刷子

       SelectObject (hdc, hbrush); //使用刷子

       画完后,我们要记住释放HDC:

       lpDDSXXX->ReleaseDC(hdc);


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值