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);
ddraw 视频下绘图 不闪烁的方法的更多相关文章
- ddraw 视频下画图 不闪烁的方法
我们如果是在在RGB视频上画图(直线,矩形等),一般采用双缓冲区继续,使用内存MemoryDC,来实现画的图形在视频上显示不闪烁的功能,但是我们知道用RGB显示视频都是使用GDI进行渲染,这样很耗CP ...
- GDI+ 绘图闪烁解决方法
闲着没事,准备做一个类似于TeeChart的自定义控件,结果第一步的绘图就把我给难倒了,虽然早就知道GDI绘图的闪烁问题很坑,但是却没有想到如此之坑,折腾了两天,才找到解决方法. 首先在窗体加载的时候 ...
- IE6-7下margin-bottom不兼容解决方法(非原创,视频中看到的)
在IE低版本下有很多不兼容,现在将看到的 IE6-7下margin-bottom不兼容解决方法 演示一下,方便日后自己查阅. <!DOCTYPE html> <html la ...
- (VC)解决绘图时闪烁问题的一点经验[转]
转自:http://www.cnblogs.com/lidabo/p/3429862.html 清除屏幕闪烁 (转自网上) <一> 由于作图过于复杂和频繁,所以时常出现闪烁的情况,一些防止 ...
- MFC 解决绘图时闪烁问题的一点经验
2015-05 由于作图过于复杂和频繁,所以时常出现闪烁的情况,一些防止闪烁的方法,如下: (1)将Invalidate()替换为InvalidateRect(). Invalidate()会导致整个 ...
- 【Visual C++】Windows GDI贴图闪烁解决方法
一般的windows 复杂的界面需要使用多层窗口而且要用贴图来美化,所以不可避免在窗口移动或者改变大小的时候出现闪烁. 先来谈谈闪烁产生的原因 原因一:如果熟悉显卡原理的话,调用GDI函数向屏幕输出的 ...
- 腾讯视频QLV格式转换mp4的方法
腾讯视频QLV格式转换mp4的方法不知道大家知不知道用?喜欢用腾讯视频的朋友应该都知道腾讯视频单独搞出了个QLV格式文件,只能用腾讯独有的腾讯视频软件才能播放,就算用格式工厂转换也不行,那么腾讯视频的 ...
- 在Ubuntu下搭建FTP服务器的方法
由于整个学校相当于一个大型局域网,相互之间传送数据非常快,比如要共享个电影,传点资料什么的. 所以我们可以选择搭建一个FTP服务器来共享文件. 那么问题来了,有的同学会问,我们既然在一个局域网内,直接 ...
- [转载]Linux下终端字体颜色设置方法
原文地址:Linux下终端字体颜色设置方法作者:router 网上类似的文章有很多,但是都是转来转去的,没有经过测试,按照很多文章的方法会造成你设置之后的终端在换行和删除输入字符时终端显示会乱七八糟, ...
随机推荐
- js进阶 12-7 如何知道你是从哪个元素移动到当前元素与事件调用时如何添加额外数据
js进阶 12-7 如何知道你是从哪个元素移动到当前元素与事件调用时如何添加额外数据 一.总结 一句话总结:event的relatedTarget属性和data属性. 1.如何知道你是从哪个元素移动到 ...
- VMware Workstation 12 安装mac os x 10.11
本人近期在学习iOS开发,由于初学,购买设备有点太昂贵了点.和我有意向想法的朋友能够看看在这篇文章.在虚拟机里装MAC os系统. 第一步:准备 VMware Workstation 12版本号 ma ...
- 线程之一:JAVA线程基础 分类: B1_JAVA 2013-10-10 12:48 662人阅读 评论(0) 收藏
参考core java,马士兵视频 1.线程的基本概念 (1)一个线程是一个程序内部的顺序控制流. (2)线程和进程 –每个进程都有独立的代码和数据空间(进程上下文),进程切换的开销大. –线程: ...
- js课程 1-5 js如何测试变量的数据类型
js课程 1-5 js如何测试变量的数据类型 一.总结 一句话总结:用typeof()方法. 1.js如何判断变量的数据类型? 用typeof()方法. 13 v=10; 14 15 if(typeo ...
- 前端js实现打印(导出)excel表格
产品原型: 图片.png 功能需求:点击导出考勤表格按钮,会自动下载成Excel格式 图片.png 图片.png jsp页面代码: <div class="tools"> ...
- BootStrap让两个控件在一行显示
<div class="row"> <div> <label class="form-inline">参加单位:<in ...
- 数据库筛选用户,然后去掉一部分(列表求差),再随机返回一个用户。sqlalchemy + python集合(set) + random
sqlalchemy和flask-sqlalchemy之间的东西不是太清晰. sqlalchemy文档太他妈多了.日. 今天遇到的实例. 用户进行随机匹配,系统随机返回一个一定筛选条件下的用户.为了用 ...
- Android MagicIndicator系列之一 —— 使用MagicIndicator打造千变万化的ViewPager指示器
说到 ViewPager 指示器,想必大家都不陌生,绝大部分应用中都有这个.使用频率非常之高.但系统对它的支持并不好,自带的 PagerTabStrip 和 PagerTitleStrip 太弱,很难 ...
- 【LeetCode-面试算法经典-Java实现】【107-Binary Tree Level Order Traversal II(二叉树层序遍历II)】
[107-Binary Tree Level Order Traversal II(二叉树层序遍历II)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a ...
- js进阶 10-10 可见伪类选择器和内容伪类选择器的作用
js进阶 10-10 可见伪类选择器和内容伪类选择器的作用 一.总结 一句话总结:分组来描述.内容伪类选择器就是 四个 包含.可见的伪类选择器就是可见和不可见.查找功能,也就是内容伪类选择器非常 ...