zoukankan      html  css  js  c++  java
  • 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
    ");
    		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
    ");
    		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);


     

  • 相关阅读:
    SpringMVC总结
    Linux执行.sh文件,提示No such file or directory的问题的解决方法
    eclipse下搭建shell脚本编辑器--安装开发shell的eclipse插件shelled
    Windows下调试hadoop
    【转】Markdown 的一些问题
    【转】论“我”和“我们”
    【转】什么是启发
    【转】Scheme 编程环境的设置
    【转】我为什么离开 Cornell
    【转】Currying 的局限性
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/4495274.html
Copyright © 2011-2022 走看看