多媒体编程——绘制图像
2014-06-04 23:36
211 查看
多媒体编程——绘制图像
首先来说,最常见的图像文件类型有bmp,jpg,png等等。每一种都有他们各自文件格式和特点。
但是对于多媒体级别的图像来说(硬件加速连续快速的渲染不同的图像),这几种格式都不能直接渲染。
在windows上,图像渲染的方式有多种,API的抽象程度也不一样,而最常用的两种当然是使用GDI 和 DirectDraw。GDI底层也是使用的DirectDraw,所以我们就直接以DirectDraw来讲解。
DirectSDK中DirectDraw版本问题,前一节已经讲过。
DirectDraw支持的格式,和上面提到的bmp,jpg等文件格式,没有任何关系,DirectDraw支持的格式是指的像素格式,而bmp,jpg等等为文件压缩格式。
什么是像素格式?
比如RGBA 就是像素格式。就是定义描述像素点的格式
除了RGBA以外,最常用的还是YUV格式。
什么是YUV? Y表示亮度,UV表示红色和绿色(不太确定,反正就是红绿蓝中的两个)。
不需要太纠结YUV具体参数的含义,因为 YUV 是三个分量,和RGB 是等价的,可以相互转换。
YUV的存在是为了兼容黑白视频,因为如果没有UV,只有Y,依然能够看到图像。而RGB格式则不支持黑白画面。
RGB分为 RGB24 RGB565,RGB32等等
而YUV也分为很多种 比如YV12,YUYV什么什么的,具体的请查询相关资料。
下面重点讲怎么使用DirectDraw。
先讲整个流程,最后贴一个完整的工具类代码上来。在下一节,将完整的讲一个从摄像头取图像并绘制到窗口的例子程序,到时此部分代码将在例子程序中找到。
使用DirectDraw将有如下步骤:
1、初始化DirectDraw对象。
2、设置协作等级,协作等级的HWND,一般为当前应用程序的最外层HWND。
3、创建主表面
4、// 创建缓存表面
为什么后面要尝试一次使用DDSCAPS_SYSTEMMEMORY 呢,因为如果是集成显卡,第一次创建使用DDSCAPS_VIDEOMEMORY 会失败。如果使用显卡内存不行,就使用系统内存。
5、创建裁剪类,并关联到HWND
裁剪类是干嘛的呢?比方说你当前的QQ聊天窗口挡住了播放器窗口,如果不加裁剪工具,那么播放器的视频画面就会显示到聊天窗口上。m_hPlayWnd 是要显示图像的那个HWND 。
初始化过程已经完了,下面将如何渲染图像。
对于像素格式类说,有两大类,一种叫面格式,一种叫线格式。还有的人叫打包格式,术语这方面我不讲究,参考参考就行
比如我有4个像素点组成的图像。 每个像素点都有个YUV。
平面格式的话,数据的排列方式是这样的 YYYY UUUU VVVV = 12个字节。
线格式或者打包格式的话,数据排列可能是这样的 YUV YUV YUV YUV = 12个字节。
下面是两个函数。 分别绘制两种格式。
看不懂没关系,对于视频渲染来说,就像素格式那点就会把人搞晕。期待下一节吧。
以读取摄像头的画面,经过格式转换,并且一DirectDraw方式输出显示。
首先来说,最常见的图像文件类型有bmp,jpg,png等等。每一种都有他们各自文件格式和特点。
但是对于多媒体级别的图像来说(硬件加速连续快速的渲染不同的图像),这几种格式都不能直接渲染。
在windows上,图像渲染的方式有多种,API的抽象程度也不一样,而最常用的两种当然是使用GDI 和 DirectDraw。GDI底层也是使用的DirectDraw,所以我们就直接以DirectDraw来讲解。
DirectSDK中DirectDraw版本问题,前一节已经讲过。
DirectDraw支持的格式,和上面提到的bmp,jpg等文件格式,没有任何关系,DirectDraw支持的格式是指的像素格式,而bmp,jpg等等为文件压缩格式。
什么是像素格式?
比如RGBA 就是像素格式。就是定义描述像素点的格式
除了RGBA以外,最常用的还是YUV格式。
什么是YUV? Y表示亮度,UV表示红色和绿色(不太确定,反正就是红绿蓝中的两个)。
不需要太纠结YUV具体参数的含义,因为 YUV 是三个分量,和RGB 是等价的,可以相互转换。
YUV的存在是为了兼容黑白视频,因为如果没有UV,只有Y,依然能够看到图像。而RGB格式则不支持黑白画面。
RGB分为 RGB24 RGB565,RGB32等等
而YUV也分为很多种 比如YV12,YUYV什么什么的,具体的请查询相关资料。
下面重点讲怎么使用DirectDraw。
先讲整个流程,最后贴一个完整的工具类代码上来。在下一节,将完整的讲一个从摄像头取图像并绘制到窗口的例子程序,到时此部分代码将在例子程序中找到。
使用DirectDraw将有如下步骤:
1、初始化DirectDraw对象。
LPDIRECTDRAW7 m_lpDDraw; // DirectDraw 对象指针 LPDIRECTDRAWSURFACE7 m_lpDDSPrimary; // DirectDraw 主表面指针 LPDIRECTDRAWSURFACE7 m_lpDDSOverlay; // DirectDraw 离屏表面指针 DDSURFACEDESC2 m_stPrimaryDdsd; // DirectDraw 表面描述 DDSURFACEDESC2 m_stOverlayDdsd; // DirectDraw 表面描述 LPDIRECTDRAWCLIPPER m_pDirectDrawCliper ;/ DirectDrawCreateEx(NULL, (LPVOID*)&m_lpDDraw,IID_IDirectDraw7, NULL);
2、设置协作等级,协作等级的HWND,一般为当前应用程序的最外层HWND。
m_lpDDraw->SetCooperativeLevel(m_hMainWnd, DDSCL_NORMAL);
3、创建主表面
DDSURFACEDESC2 m_stPrimaryDdsd; ZeroMemory(&m_stPrimaryDdsd, sizeof(DDSURFACEDESC2)); m_stPrimaryDdsd.dwSize = sizeof(DDSURFACEDESC2); m_stPrimaryDdsd.dwFlags = DDSD_CAPS ; m_stPrimaryDdsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; hr = m_lpDDraw->CreateSurface(&m_stPrimaryDdsd, &m_lpDDSPrimary, NULL);
4、// 创建缓存表面
ZeroMemory(&m_stOverlayDdsd, sizeof(DDSURFACEDESC2)); m_stOverlayDdsd.dwSize = sizeof(DDSURFACEDESC2); m_stOverlayDdsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OFFSCREENPLAIN ; m_stOverlayDdsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT ; m_stOverlayDdsd.dwWidth = m_dwWidth; m_stOverlayDdsd.dwHeight = m_dwHeight; m_stOverlayDdsd.ddpfPixelFormat = pixfmt ; m_stOverlayDdsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); hr = m_lpDDraw->CreateSurface(&m_stOverlayDdsd, &m_lpDDSOverlay, NULL); if (FAILED(hr)) { m_stOverlayDdsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY | DDSCAPS_OFFSCREENPLAIN ;//使用内存再次尝试创建。 hr = m_lpDDraw->CreateSurface(&m_stOverlayDdsd, &m_lpDDSOverlay, NULL); if (FAILED(hr)) return hr ; }
为什么后面要尝试一次使用DDSCAPS_SYSTEMMEMORY 呢,因为如果是集成显卡,第一次创建使用DDSCAPS_VIDEOMEMORY 会失败。如果使用显卡内存不行,就使用系统内存。
5、创建裁剪类,并关联到HWND
hr = m_lpDDraw->CreateClipper(0,&m_pDirectDrawCliper,NULL); if (FAILED(hr)) return hr; hr = m_pDirectDrawCliper->SetHWnd(0,m_hPlayWnd); if (FAILED(hr)) return hr; hr = m_lpDDSPrimary->SetClipper(m_pDirectDrawCliper); if (FAILED(hr)) return hr;
裁剪类是干嘛的呢?比方说你当前的QQ聊天窗口挡住了播放器窗口,如果不加裁剪工具,那么播放器的视频画面就会显示到聊天窗口上。m_hPlayWnd 是要显示图像的那个HWND 。
初始化过程已经完了,下面将如何渲染图像。
对于像素格式类说,有两大类,一种叫面格式,一种叫线格式。还有的人叫打包格式,术语这方面我不讲究,参考参考就行
比如我有4个像素点组成的图像。 每个像素点都有个YUV。
平面格式的话,数据的排列方式是这样的 YYYY UUUU VVVV = 12个字节。
线格式或者打包格式的话,数据排列可能是这样的 YUV YUV YUV YUV = 12个字节。
下面是两个函数。 分别绘制两种格式。
HRESULT CDXImageDrawer::DisplayPlane(LPCBYTE pBuf[3],DWORD dwWidthDeflate[3],DWORD dwHeightDeflate[3]) { HRESULT hr; // DirectDraw 函数返回值 hr = m_lpDDSOverlay->Lock(NULL,&m_stOverlayDdsd,DDLOCK_WAIT|DDLOCK_WRITEONLY,NULL); if (FAILED(hr)) return hr; LPCBYTE Y = pBuf[0]; LPCBYTE U = pBuf[1]; LPCBYTE V = pBuf[2]; LPBYTE lpSurface = (LPBYTE)m_stOverlayDdsd.lpSurface; if(lpSurface) { //Y DWORD dwLineWidth = m_stOverlayDdsd.dwWidth >> dwWidthDeflate[0] ; DWORD dwLineHeight = m_stOverlayDdsd.dwHeight >> dwHeightDeflate[0] ; DWORD dwLinePitch = m_stOverlayDdsd.lPitch >> dwWidthDeflate[0] ; for (DWORD i=0;i<dwLineHeight;i++) { memcpy(lpSurface, Y, dwLineWidth); Y += dwLineWidth; lpSurface += dwLinePitch; } //V dwLineWidth = m_stOverlayDdsd.dwWidth >> dwWidthDeflate[2] ; dwLineHeight = m_stOverlayDdsd.dwHeight >> dwHeightDeflate[2] ; dwLinePitch = m_stOverlayDdsd.lPitch >> dwWidthDeflate[2] ; for (DWORD i=0;i<dwLineHeight;i++) { memcpy(lpSurface, V, dwLineWidth); V += dwLineWidth; lpSurface += dwLinePitch; } //U dwLineWidth = m_stOverlayDdsd.dwWidth >> dwWidthDeflate[1] ; dwLineHeight = m_stOverlayDdsd.dwHeight >> dwHeightDeflate[1] ; dwLinePitch = m_stOverlayDdsd.lPitch >> dwWidthDeflate[1] ; for (DWORD i=0;i<dwLineHeight;i++) { memcpy(lpSurface, U, dwLineWidth); U += dwLineWidth; lpSurface += dwLinePitch; } } hr = m_lpDDSOverlay->Unlock(NULL); if (FAILED(hr)) return hr; RECT rect; ::GetClientRect(m_hPlayWnd,&rect); POINT point = {0,0}; ::ClientToScreen(m_hPlayWnd,&point); rect.right = rect.right - rect.left + point.x; rect.left = point.x; rect.bottom = rect.bottom - rect.top + point.y; rect.top = point.y; hr = m_lpDDSPrimary->Blt(&rect, m_lpDDSOverlay, NULL, DDBLT_WAIT, NULL); if (FAILED(hr)) return hr; return S_OK ; } HRESULT CDXImageDrawer::DisplayLine(LPCBYTE pBuf,int nPixelBytes) { HRESULT hr; // DirectDraw 函数返回值 hr = m_lpDDSOverlay->Lock(NULL,&m_stOverlayDdsd,DDLOCK_WAIT|DDLOCK_WRITEONLY,NULL); if (FAILED(hr)) return hr; LPCBYTE p_YUV_RGB = pBuf ; DWORD dwByteCountPerLine = m_stOverlayDdsd.dwWidth*nPixelBytes; //m_stOverlayDdsd.ddpfPixelFormat.dwRGBBitCount/8 ; LPBYTE lpSurface = (LPBYTE)m_stOverlayDdsd.lpSurface; if(lpSurface) { for (DWORD i=0;i<m_stOverlayDdsd.dwHeight;i++) { memcpy(lpSurface, p_YUV_RGB, dwByteCountPerLine); p_YUV_RGB += dwByteCountPerLine; lpSurface += m_stOverlayDdsd.lPitch; } } hr = m_lpDDSOverlay->Unlock(NULL); if (FAILED(hr)) return hr; RECT rect; ::GetClientRect(m_hPlayWnd,&rect); POINT point = {0,0}; ::ClientToScreen(m_hPlayWnd,&point); rect.right = rect.right - rect.left + point.x; rect.left = point.x; rect.bottom = rect.bottom - rect.top + point.y; rect.top = point.y; hr = m_lpDDSPrimary->Blt(&rect, m_lpDDSOverlay, NULL, DDBLT_WAIT, NULL); if (FAILED(hr)) return hr; return S_OK ; }
看不懂没关系,对于视频渲染来说,就像素格式那点就会把人搞晕。期待下一节吧。
以读取摄像头的画面,经过格式转换,并且一DirectDraw方式输出显示。
相关文章推荐
- 多媒体编程——ios视频图像绘制(2)
- 多媒体编程——ios视频图像绘制(1)
- 数字图像处理编成入门笔记——第10章 图象处理编程工具及简单的多媒体编程
- bada 2D游戏编程之二――图像绘制(1)
- Android多媒体高级编程(一)——Camera和简单的图像处理
- Android编程实现扭曲图像的绘制功能示例
- Quartz2D 编程指南(四)位图与图像遮罩、CoreGraphics 绘制 Layer
- 我的opengl编程学习(一)(简介、绘制图像、三维观察、光照)
- 多媒体编程——ios摄像头图像抓取工具类
- bada 2D游戏编程之二――图像绘制(2)
- C语言编程实战图形图像部分第一节:简单图行绘制
- anddroid 图形图像编程- 第二章 直线和曲线的绘制
- 多媒体编程ios摄像头图像抓取工具类
- Android多媒体开发 Pro Android Media 第一章 Android图像编程入门 4
- Delphi初浅入门笔记之十二:多媒体编程五(绘制文字篇)
- Windwos 编程源码例子下载:包括界面编程,网路编程,图像编程,多媒体编程等等
- Android多媒体开发 Pro Android Media 第一章 Android图像编程入门 2
- JAVA多媒体编程入门(图像部分)
- win32编程,将rgb图像绘制到窗口句柄
- JAVA多媒体编程入门(图像部分)