我們如果是在在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);