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到主表面,这样画的图形就不会闪烁了。

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

[cpp] view plaincopy

  1. #ifndef _DIRECTDRAW_H_
  2. #define _DIRECTDRAW_H_
  3. #pragma once
  4. #include "ddraw.h"
  5. #define   FOURCC_YUYV   0x32595559   //   MAKEFOURCC( ‘Y ‘, ‘U ‘, ‘Y ‘, ‘2 ‘)
  6. #define   FOURCC_UYVY   0x59565955   //   MAKEFOURCC( ‘U ‘, ‘Y ‘, ‘V ‘, ‘Y ‘)
  7. #define  YUV_UYVY 1
  8. #define  YUV_YUYV 2
  9. class __declspec(dllexport) CDirectDraw
  10. {
  11. public:
  12. CDirectDraw(void);
  13. ~CDirectDraw(void);
  14. bool DirectDrawInit(HWND hWnd, int width, int height,DWORD dwYuvFourCC);
  15. bool DisPlayYUVData(byte *pYUVData,int bYuvType,RECT rect);

[cpp] view plaincopy

  1. void DirectDrawDeInit(void);

[cpp] view plaincopy

  1. protected:
  2. LPDIRECTDRAW7           lpDD;                                           // DirectDraw 对象指针
  3. LPDIRECTDRAWSURFACE7    lpDDSPrimary;   // DirectDraw 主表面指针
  4. LPDIRECTDRAWSURFACE7    lpDDSOffScr;        // DirectDraw 离屏表面指针
  5. DDSURFACEDESC2          ddsd;   // DirectDraw 表面描述
  6. LPDIRECTDRAWSURFACE7 m_pOsdSurface;     //画图表面
  7. };
  8. #endif

DirectDraw.cpp 源文件如下:

[cpp] view plaincopy

  1. #include "StdAfx.h"
  2. #include "DirectDraw.h"
  3. #include <string>
  4. using namespace std;
  5. CDirectDraw::CDirectDraw(void)
  6. {
  7. lpDD=NULL;            // DirectDraw 对象指针
  8. lpDDSPrimary=NULL;   // DirectDraw 主表面指针
  9. lpDDSOffScr=NULL;   // DirectDraw 离屏表面指针
  10. m_pOsdSurface=NULL;
  11. }
  12. CDirectDraw::~CDirectDraw(void)
  13. {
  14. DirectDrawDeInit();
  15. }
  16. //yuv_type控件不同的yuv格式

[cpp] view plaincopy

  1. bool CDirectDraw::DirectDrawInit(HWND hWnd, int width, int height,DWORD dwYuvFourCC)
  2. {
  3. HRESULT hr;
  4. // 创建DirectCraw对象
  5. if (DirectDrawCreateEx(NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL) != DD_OK)
  6. {
  7. return false;
  8. }
  9. // 设置协作层
  10. if (lpDD->SetCooperativeLevel(hWnd,
  11. DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES) != DD_OK)
  12. {
  13. return false;
  14. }
  15. // 创建主表面
  16. LPVOID  lpSurface  = NULL;
  17. ZeroMemory(&ddsd, sizeof(ddsd));
  18. ZeroMemory(&ddsd.ddpfPixelFormat, sizeof(DDPIXELFORMAT));
  19. ddsd.dwSize = sizeof(ddsd);
  20. ddsd.dwFlags = DDSD_CAPS;
  21. ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
  22. if (lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL) != DD_OK)
  23. {
  24. return false;
  25. }
  26. LPDIRECTDRAWCLIPPER   pcClipper;    // Cliper
  27. if( lpDD->CreateClipper( 0, &pcClipper, NULL ) != DD_OK )
  28. return false;
  29. if( pcClipper->SetHWnd( 0, hWnd ) != DD_OK )
  30. {
  31. pcClipper->Release();
  32. return false;
  33. }
  34. if( lpDDSPrimary->SetClipper( pcClipper ) != DD_OK )
  35. {
  36. pcClipper->Release();
  37. return false;
  38. }
  39. // Done with clipper
  40. pcClipper->Release();
  41. // 创建YUV表面
  42. ddsd.ddsCaps.dwCaps =  DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY ;
  43. ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT ;
  44. ddsd.dwWidth = width;
  45. ddsd.dwHeight =height;
  46. ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
  47. ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_YUV ;
  48. ddsd.ddpfPixelFormat.dwYUVBitCount = 8;
  49. ddsd.ddpfPixelFormat.dwFourCC =dwYuvFourCC;
  50. hr=lpDD->CreateSurface(&ddsd, &lpDDSOffScr, NULL);
  51. if ( hr!= DD_OK)
  52. {
  53. return false;
  54. }
  55. #if 1
  56. //创建OSD画图离屏表面
  57. //
  58. ZeroMemory(&ddsd, sizeof(ddsd));
  59. ddsd.dwSize = sizeof(ddsd);
  60. ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
  61. ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
  62. ddsd.dwWidth = width;
  63. ddsd.dwHeight = height;
  64. hr = lpDD->CreateSurface(&ddsd, &m_pOsdSurface, NULL);
  65. if ( hr != DD_OK)
  66. {
  67. //lpDD->Release();
  68. //lpDDSPrimary = NULL;
  69. //lpDD = NULL;
  70. return false;
  71. }
  72. #endif
  73. return true;
  74. }
  75. //add rect参数,将图像缩放到rect内
  76. bool CDirectDraw::DisPlayYUVData(byte *pYUVdata,int bYuvType,RECT rect)
  77. {
  78. byte *pSurf;
  79. int yuv_type=bYuvType;
  80. HRESULT hr;
  81. hr=lpDDSOffScr->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT,NULL);
  82. //2012-02-24
  83. if (hr==DDERR_SURFACELOST)
  84. {
  85. TRACE("off surface lost,restore offscr\n");
  86. hr=lpDDSOffScr->Restore();
  87. hr=lpDDSOffScr->Lock(NULL,&ddsd,DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT,NULL);
  88. }
  89. if (FAILED(hr))
  90. {
  91. return DD_FALSE;
  92. }
  93. //2012-02-11
  94. if (yuv_type==YUV_UYVY)
  95. {
  96. ddsd.ddpfPixelFormat.dwFourCC =FOURCC_UYVY;
  97. }else
  98. {
  99. ddsd.ddpfPixelFormat.dwFourCC =FOURCC_YUYV;
  100. }
  101. pSurf=(LPBYTE)ddsd.lpSurface;
  102. if (pSurf)
  103. {
  104. for(unsigned int i=0; i < ddsd.dwHeight; i++)
  105. {
  106. memcpy(pSurf,pYUVdata,ddsd.dwWidth*2);
  107. pYUVdata+=ddsd.dwWidth*2;
  108. pSurf+=ddsd.lPitch;
  109. }
  110. }
  111. lpDDSOffScr->Unlock(NULL);
  112. #if 1
  113. //加入Osd离屏表面内容
  114. HRESULT ddrval;
  115. ddrval = m_pOsdSurface->Blt(&rect, lpDDSOffScr, NULL, DDBLT_WAIT, NULL);
  116. if (ddrval != DD_OK)
  117. {
  118. ddrval = lpDDSPrimary->Blt(&rect, lpDDSOffScr, &rect, DDBLT_WAIT, NULL);
  119. }
  120. else
  121. {
  122. HDC hDC = NULL;
  123. ddrval = m_pOsdSurface->GetDC(&hDC);
  124. if ((ddrval == DD_OK)&&(hDC != NULL))
  125. {
  126. //叠加文字
  127. SetTextColor(hDC,RGB(255,0,0));
  128. SetBkColor(hDC,RGB(0,255,0));
  129. CString m_sOsdMsg=_T("hello world");
  130. TextOut(hDC, rect.left+100,rect.top+200 , m_sOsdMsg, m_sOsdMsg.GetLength());
  131. //画实心矩形
  132. HPEN hpen = CreatePen (PS_SOLID, 1, RGB(255, 0, 0));
  133. SelectObject (hDC, hpen);
  134. HBRUSH hbrush = CreateSolidBrush (RGB(0, 255, 0)); //创建刷子
  135. SelectObject (hDC, hbrush);                        //使用刷子
  136. Rectangle(hDC, rect.left+100, rect.top+100, rect.left+200, rect.top+200); //画矩形
  137. //画空心矩形
  138. RECT rect1;
  139. rect1.left=rect.left+200;
  140. rect1.top=rect.top+200;
  141. rect1.right=rect.left+300;
  142. rect1.bottom=rect.top+300;
  143. FrameRect(hDC,&rect1,CreateSolidBrush(RGB(255,0,0)));
  144. //画直线
  145. MoveToEx(hDC,rect.left+50,rect.top+50,NULL);
  146. LineTo(hDC,rect.left+350,rect.top+350);
  147. m_pOsdSurface->ReleaseDC(hDC);
  148. lpDDSPrimary->Blt(&rect, m_pOsdSurface, &rect, DDBLT_WAIT, NULL);
  149. }
  150. }
  151. #else
  152. //只有主表面和离屏表面
  153. HRESULT ddrval;
  154. ddrval=lpDDSPrimary->Blt(&rect, lpDDSOffScr, NULL, DDBLT_WAIT, NULL);
  155. if (ddrval==DDERR_SURFACELOST)
  156. {
  157. TRACE("primary surface lost,restore all surfaces\n");
  158. lpDDSPrimary->Restore();
  159. }
  160. #endif
  161. return DD_OK;
  162. }
  163. //ddraw deInit
  164. void CDirectDraw::DirectDrawDeInit(void)
  165. {
  166. if (lpDDSOffScr != NULL)
  167. {
  168. lpDDSOffScr->Release();
  169. lpDDSOffScr = NULL;
  170. }
  171. if (lpDDSPrimary != NULL)
  172. {
  173. lpDDSPrimary->Release();
  174. lpDDSPrimary = NULL;
  175. }
  176. if (lpDD != NULL)
  177. {
  178. lpDD->Release();
  179. lpDD = NULL;
  180. }
  181. if (m_pOsdSurface!=NULL)
  182. {
  183. m_pOsdSurface->Release();
  184. m_pOsdSurface=NULL;
  185. }
  186. }

测试结果如下:

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);

时间: 2024-10-13 02:08:33

ddraw 视频下画图 不闪烁的方法的相关文章

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

我们假设是在在RGB视频上绘图(直线,矩形等),一般採用双缓冲区继续,使用内存MemoryDC,来实现画的图形在视频上显示不闪烁的功能,可是我们知道用RGB显示视频都是使用GDI进行渲染,这样非常耗CPU,那么我们能不能在YUV上进行视频渲染呢,答案是肯定的,使用ddraw直接显示yuv就ok了,能够支持yuv422和yuv420的直接使用显卡显示,不耗CPU,可是我们在使用ddraw显示时,然后在配合GDI绘图(直线或矩形等),画的图形是闪烁的,原因是我们在ddraw直接显示yuv视频时,使用

【Visual C++】Windows GDI贴图闪烁解决方法

一般的windows 复杂的界面需要使用多层窗口而且要用贴图来美化,所以不可避免在窗口移动或者改变大小的时候出现闪烁. 先来谈谈闪烁产生的原因 原因一:如果熟悉显卡原理的话,调用GDI函数向屏幕输出的时候并不是立刻就显示在屏幕上只是写到了显存里,而显卡每隔一段时间把显存的内容输出到屏幕上,这就是刷新周期. 一般显卡的刷新周期是 1/80秒左右,具体数字可以自己设置的. 这样问题就来了,一般画图都是先画背景色,然后再把内容画上去,如果这两次操作不在同一个刷新周期内完成,那么给人的视觉感受就是,先看

避免闪烁的方法(OnEraseBkgnd)

在图形图象处理编程过程中,双缓冲是一种基本的技术.我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象.解决这一问题的有效方法就是双缓冲技术. 因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差.当WM_PAINT的响应很频繁的时候,这种反差也就越发明显.于是我们就看到了闪烁现象. 我们会很自然的想到,避免背景色的填充是

Angular.js中处理页面闪烁的方法详解

Angular.js中处理页面闪烁的方法详解 前言 大家在使用{{}}绑定数据的时候,页面加载会出现满屏尽是{{xxx}}的情况.数据还没响应,但页面已经渲染了.这是因为浏览器和angularjs渲染页面都需要消耗一定的时间,这个间隔可能很小,甚至让人感觉不到,这种情况一切正常,但这个时间也可能很长,这时候用户可能会看到满屏尽是{{xxxx}}.这种情况被叫做"Flash Of Unrendered Content (FOUC)(K)?and is always unwanted.".

winform频繁刷新导致界面闪烁解决方法

转自龙心文 原文 winform频繁刷新导致界面闪烁解决方法 一.通过对窗体和控件使用双缓冲来减少图形闪烁(当绘制图片时出现闪烁时,使用双缓冲) 对于大多数应用程序,.NET Framework 提供的默认双缓冲将提供最佳效果.默认情况下,标准 Windows 窗体控件是双缓冲的.可以通过两种方法对窗体和所创作的控件启用默认双缓冲.一种方法是将 DoubleBuffered 属性设置为 true,另一种方法是通过调用 SetStyle 方法将 OptimizedDoubleBuffer 标志设置

利用C语言编辑画图程序的实现方法

不知道大家在进行开发县级电网调度自动化系统的时候,是否都会遇到一个问题就是:要绘制一个电力系统一次接线图.大家都应该知道其实电力系统的一次接线图是较为复杂的,如果想要使用一般的编程方法来进行绘制的话,基本上就是行不通的.那么我们应该怎样才可以更加的高效直接呢?今天小编就会给大家介绍一个方法,那就是:利用C语言编辑画图程序的实现方法.希望这篇教程对于大家有所帮助. 一.实现方法 在教程开始之前,小编先为大家介绍一下在编程程序里面早已定义了几个特殊按钮.为什么小编要为大家介绍这几个特殊按钮呢?那是因

Linux培训教程 linux系统下分割大文件的方法

在linux中分割大文件,比如一个5gb日志文件,需要把它分成多个小文件,分割后以利于普通的文本编辑器读取. 有时,需要传输20gb的大文件,Linux培训 教程件到另一台服务器,也需要把它分割成多个文件,这样便于传输数据. 以下通过五个不同的例子,来讲解Linux下分割大文件的方法,供大家参考. 例1.以每个文件1000行分割 split命令分割文件成每个文件1000行,并且文件名依次为 [前缀]aa,[前缀]ab, [前缀]ac等,默认的前缀是X,每个文件的行数为1000行. 命令: 复制代

Windows系统下Python与NumPy安装方法

Windows系统下Python与NumPy安装方法 Windows下Python的某些第三方包安装步骤实在是太麻烦了(这里主要以NumPy为例,目前只有遇到安装它的时候出现了很多问题),晚上花了好几个小时才把NumPy科学计算包安装好,在这里描述下安装过程,避免大家走没有必要的弯路. 1,安装Python 首先,运行下载的MSI安装包,选择安装组件时,确保勾上了所有的组件. 特别要注意选上pip和Add python.exe to Path,然后多次点击Next即可完成安装. Python解释

android设计的布局在阿拉伯语下界面错乱的解决方法

(1)正在AndroidManifest.xml声明文件的application元素中,增加" android:supportsRtl=true" (2)建] androidの设计的布局在阿拉伯语下界面错乱的解决方法 (1)在AndroidManifest.xml声明文件的元素中,添加" android:supportsRtl=true" (2)修改应用程序中layout的"left/right"布局属性,改为对应的"start/end