레이블이 directdraw인 게시물을 표시합니다. 모든 게시물 표시
레이블이 directdraw인 게시물을 표시합니다. 모든 게시물 표시

2015년 11월 27일 금요일

DirectDraw로 draw시 깜빡임 현상 방지

DirectDraw 로 영상을 출력할때 모니터 주파수에 따라 출력 영상 깜빡임 현상이 발생하는데 이것은 draw 주기가 서로 맞지않기 때문이다.
IDirectDraw7::WaitForVerticalBlank() 메소드를 사용해서 주기를 맞춰주면 깜빡임을 막을 수 있다.

// synchronize with vertical-blank interval
if (m_pDDraw[index].isFirstDisplay) {
pDDraw->pDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL);
m_pDDraw[index].isFirstDisplay = false;
}

2012년 10월 8일 월요일

DirectDraw Surface 에 Win32 GDI 함수로 그리기 - Drawing on DirectDraw Surface with Win32 GDI API


void drawSurface(RECT *rectTarget, IDirectDrawSurface7 *lpSurface)
{
HDC hdc;
hr=lpSurface->GetDC(&hdc);

int targetWidth = rectTarget->right-rectTarget->left;
int targetHeight = rectTarget->bottom-rectTarget->top;

HDC hMemDC;

hMemDC = CreateCompatibleDC(hdc);

HBITMAP hbmMem = CreateCompatibleBitmap(hdc, targetWidth, targetHeight);
SelectObject(hMemDC, hbmMem);
 .....
 .....
 TextOut(hMemDC, ....);
 .....
 TransparentBlt(hMemDC, ...);
 .....
 Rectangle(hMemDC, ...);
 ....
 ....

DeleteObject(hbmMem);
DeleteDC(hMemDC);

lpSurface->ReleaseDC(hdc);
}


* lpSurface->GetDC()/lpSurface->ReleaseDC() 하면서 lpSurface 는 lock/unlock 이 된다. 
따라서 위 함수 내부에서 DirectDraw Blt 함수가 호출되어서는 안된다.

* Win32 GDI 함수는 기본적으로 DirectDraw 계열 함수보다 훨씬 느리기때문에 위 함수를
사용할때는 두 개의 Surface를 두고 Blt 하는 Double-buffering 방식으로 구현하는것이 좋다.

2012년 9월 4일 화요일

DirectDraw Surface Transparent Blt


void CopySurface(IDirectDrawSurface7 *lpSurfaceDst, RECT &rectTarget, IDirectDrawSurface7 *lpSurfaceSrc, RECT &rectSrc)
{
DDBLTFX ddbltfx;
ZeroMemory(&ddbltfx, sizeof(ddbltfx));
ddbltfx.dwSize = sizeof(ddbltfx);

DDCOLORKEY colorKey = {RGB(0,0,0), RGB(0,0,0)};    // Transparent Color - black
ddbltfx.ddckSrcColorkey = colorKey;

DWORD flags = DDBLT_ASYNC | DDBLT_KEYSRCOVERRIDE;

HRESULT hr = lpSurfaceDst->Blt(&rectTarget, lpSurfaceSrc, &rectSrc, flags, &ddbltfx);
if (FAILED(hr)) {
TRACE("[%s] hr:0x%x\n", __FUNCTION__, hr);
}
}

DirectDraw Surface 알파블랜딩 - DirectDraw Surface Alpha Blending


HBITMAP m_hbm;
BITMAP m_bmpInfo;
...

< 비트맵 로드 - bitmap load >


m_hbm = (HBITMAP) LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(123), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
GetObject(m_hbm, sizeof(m_bmpInfo), &m_bmpInfo);



< 알파블랜딩 - alpha blending >

void drawBitmapAlpha(RECT *rectTarget, IDirectDrawSurface7 *lpSurface)
{
HDC hdc;
if (lpSurface->GetDC(&hdc) != DD_OK || m_hbm == NULL)
return;

/* calculate bitmap size */
int targetWidth = rectTarget->right-rectTarget->left;
int targetHeight = rectTarget->bottom-rectTarget->top;
int cx = rectTarget->left+targetWidth/2;
int cy = rectTarget->top+targetHeight/2;

int bmpWidth = m_bmpInfo.bmWidth;
int bmpHeight = m_bmpInfo.bmHeight;

POINT pt;
pt.x = cx-bmpWidth/2;
pt.y = cy-bmpHeight/2;

HDC hMemDC, hAlphaDC;
BOOL ret;

hMemDC = CreateCompatibleDC(hdc);    // create bitmap dc
hAlphaDC = CreateCompatibleDC(hdc);    // create alpha dc

HBITMAP hbmAlpha = CreateCompatibleBitmap(hdc, bmpWidth, bmpHeight);

SelectObject(hMemDC, m_hbm);
SelectObject(hAlphaDC, hbmAlpha);
 
        // copy src image to alpha dc
ret = BitBlt(hAlphaDC, 0, 0, bmpWidth, bmpHeight, hdc, pt.x, pt.y, SRCCOPY);
if (!ret) {
TRACE("[%s] BitBlt failed, err:%d\n", __FUNCTION__, GetLastError());
}

        // copy bitmap image to alpha dc with transparent
ret = TransparentBlt(hAlphaDC, 0, 0, bmpWidth, bmpHeight, hMemDC, 0, 0, m_bmpInfo.bmWidth, m_bmpInfo.bmHeight, RGB(0, 0, 0));
if (!ret) {
TRACE("[%s] TransparentBlt failed, err:%d\n", __FUNCTION__, GetLastError());
}

BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 100;
bf.AlphaFormat = 0;

        // alpha blend alpha dc to dest dc
ret = AlphaBlend(hdc, pt.x, pt.y, bmpWidth, bmpHeight, hAlphaDC, 0, 0, bmpWidth, bmpHeight, bf);
if (!ret) {
TRACE("[%s] AlphaBlend failed, err:%d\n", __FUNCTION__, GetLastError());
}

DeleteObject(hbmAlpha);
DeleteDC(hAlphaDC);
DeleteDC(hMemDC);

lpSurface->ReleaseDC(hdc);
}

2012년 4월 27일 금요일

ffmpeg 스케일러와 DirectDraw 를 이용한 YUV -> RGB 고속변환 출력 - YUV -> RGB Converting by ffmpeg and DirectDraw

YUV420->RGB32 변환출력의 경우 보통 이전에 포스팅된 YUV->RGB 변환방식을 사용하면된다. YUV420 버퍼를 YUV 평면에 그데로 복사하고 primary 평면에 blt 를 하면 yuv->rgb 변환과 스케일링을 동시에 고속으로 수행할 수 있다.

이번에 사용하는 방법은 YUV420 버퍼를 일반 RGB32 offscreen 평면에 스케일링한 후에  primary 평면에 blt 하는 방법이다.
이 방식은 기존 YUV->RGB 변환 방식과 성능이 동일하게 나오지만 비디오카드의 하드웨어 가속기를 사용하지않기 때문에 다른 프로세스가 하드웨어 가속기를 점유하고 있을경우에도 문제없이 사용할수 있다.

ffmpeg 스케일러 사용법과 DirectDraw 사용방법은 이전 포스팅 참조

< DirectDraw 평면 생성 >
...

ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize         = sizeof( ddsd );
ddsd.dwFlags        = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

ddsd.dwWidth        = targetWidth;
ddsd.dwHeight       = targetHeight;

if ((hr=pDD->CreateSurface(&ddsd, &lpBackBuffer, NULL)) != DD_OK)
{
printf("failed to create back buffer surface (hr:0x%x)\n", hr);
return -1;
}

// 스케일러 생성
sws = sws_getContext(srcWidth, srcHeight, PIX_FMT_YUV420P, targetWidth, targetHeight, PIX_FMT_RGB32, SWS_BILINEAR, NULL, NULL, NULL);
...

< primary 평면에 출력 >

int VideoDDraw::draw(AVFrame *pPicture, RECT *rectTarget)
...
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
if ((hr=lpBackBuffer->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)) == DD_OK)
{
        // offscreen 평면에 복사 (스케일링과 동시에 복사)
LPBYTE lpSurf = (LPBYTE)ddsd.lpSurface;
LPBYTE ptr = lpSurf;
unsigned char *src_buf[4];
src_buf[0] = pPicture->data[0];
src_buf[1] = pPicture->data[1];
src_buf[2] = pPicture->data[2];
src_buf[3] = NULL;

int src_stride[4];
src_stride[0] = pPicture->linesize[0];
src_stride[1] = pPicture->linesize[1];
src_stride[2] = pPicture->linesize[2];
src_stride[3] = 0;

unsigned char *dst_buf[4];
dst_buf[0] = ptr;
dst_buf[1] = NULL;
dst_buf[2] = NULL;
dst_buf[3] = NULL;
int dst_stride[4];
dst_stride[0] = ddsd.lPitch;
dst_stride[1] = ddsd.lPitch;
dst_stride[2] = ddsd.lPitch;
dst_stride[3] = ddsd.lPitch;

        // 스케일링(변환된 데이터가 들어감)
sws_scale(sws, src_buf, src_stride, 0, h, dst_buf, dst_stride);

lpBackBuffer->Unlock(NULL);
}
...
// primary 평면에 blt
hr = lpPrimary->Blt(rectTarget, lpBackBuffer, &rectSrc, DDBLT_ASYNC, NULL);

2011년 8월 4일 목요일

멀티모니터 열거하기 - Multiple Monitor Enumeration

모니터가 여러 개인 경우 directdraw api 를 사용해 모니터별로 열거할수 있다.
getScreen() 함수는 지정한 RECT 에 해당하는 모니터를 찾고 모니터 오프셋 값을 RECT 에 넣어줌

//----------------------------------------------------------------------------------
#define  MAX_MONITOR (4)

struct Monitor {
    HMONITOR hMon;
    GUID guid; 
};

class MonitorManager {
    Monitor monitor[MAX_MONITOR];
    int nScreen;
    ...
    int getScreen(RECT *rect);
};

// Enumerate Monitor
struct EnumInfo
{
    BOOL bMultimonSupported;
    HRESULT hr;
};

BOOL WINAPI DDEnumCallbackEx( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName,
                              VOID* pContext, HMONITOR hmon )
{
MONITORINFO mi;
if (pGuid != NULL && hmon != NULL) {
mi.cbSize = sizeof(mi);
GetMonitorInfo(hmon, &mi);

MonitorManager *manager = (MonitorManager *)pContext;
manager->monitor[manager->nScreen].guid = *pGuid;
manager->monitor[manager->nScreen].hMon = hmon;
manager->nScreen++;
}
    return TRUE; // Keep enumerating
}

BOOL WINAPI DDEnumCallback( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName,
                            VOID* pContext )
{
    DDEnumCallbackEx(pGuid, pszDesc, pszDriverName, pContext, NULL);
    return FALSE; // Stop enumerating -- For this sample, we don't want any non-display devices
}

HRESULT EnumerateScreens(MonitorManager *manager)
{
    HRESULT hr;
    HMODULE hModule = NULL;
    LPDIRECTDRAWENUMERATEEX pDDEnumEx = NULL;
    EnumInfo enumInfo;

    ZeroMemory(&enumInfo, sizeof(enumInfo));

    hModule = LoadLibrary( TEXT("ddraw.dll") );
    // If ddraw.dll doesn't exist in the search path,
    // then DirectX probably isn't installed, so fail.
    if (hModule == NULL)
        return E_FAIL;
 
    pDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hModule,
#ifdef UNICODE
        "DirectDrawEnumerateExW"
#else
        "DirectDrawEnumerateExA"
#endif
        );

manager->nScreen = 0;

    if (pDDEnumEx == NULL)
    {
        // We must be running on an old version of DirectDraw.
        // Therefore MultiMon isn't supported. Fall back on
        // DirectDrawEnumerate to enumerate standard devices on a
        // single-monitor system.
        enumInfo.bMultimonSupported = FALSE;
        hr = DirectDrawEnumerate(DDEnumCallback, manager);
    }
    else
    {
        enumInfo.bMultimonSupported = TRUE;
        hr = pDDEnumEx(DDEnumCallbackEx, manager, DDENUM_ATTACHEDSECONDARYDEVICES);
    }

    // If something failed inside the enumeration, be sure to return that HRESULT

    FreeLibrary(hModule);
    return hr;
}

int MonitorManager::getScreen(RECT *rect)
{
MONITORINFO mi;
HMONITOR hMon = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST);
for (int i=0; i < nScreen; i++) {
if (hMon == monitor[i].hMon) {
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMon, &mi);
OffsetRect(rect, -mi.rcMonitor.left, -mi.rcMonitor.top);
return i;
}
}

return -1;
}

2011년 7월 8일 금요일

DirectDraw를 이용한 YUV -> RGB 고속 변환출력 - YUV -> RGB Converting by using DirectDraw

< DirectDraw YUV Surface 생성 - creating directdraw surfaces >

DDPIXELFORMAT g_ddpfFormats[] = 
{{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('Y','V','1','2'),0,0,0,0,0}}; 
...
IDirectDraw7 *pDD;
IDirectDrawSurface7 *lpPrimary;
IDirectDrawClipper      *lpClipper;
IDirectDrawSurface7   *lpYUVBuffer;
int g_width, g_height;

initDraw(HWND hWnd, int width, int height)
{
HRESULT hr;

g_width = width;
g_height = height;

hr = DirectDrawCreateEx(NULL, (void **)&pDD, IID_IDirectDraw7, NULL);
if (FAILED(hr)) {
TRACE("failed to create directdraw device (hr:0x%x)\n", hr);
return -1;
}

hr = pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
if(FAILED(hr)) {
TRACE("failed to SetCooperativeLevel (hr:0x%x)\n", hr);
return -1;
}


DDSURFACEDESC2 ddsd;

/* creating primary surface (RGB32) */
ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize         = sizeof( ddsd );
ddsd.dwFlags        = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

if (hr=pDD->CreateSurface(&ddsd, &lpPrimary, NULL ) != DD_OK)
{
TRACE("failed to create create primary surface (hr:0x%x)\n", hr);
        return -1;
}

/* creating clipper */
hr = pDD->CreateClipper(0, &lpClipper, NULL);
if (hr != DD_OK)
{
TRACE("failed to create create clipper (hr:0x%x)\n", hr);
pDD->Release();
return -1;
}
hr = lpClipper->SetHWnd(0, hWnd);
if (hr != DD_OK)
{
TRACE("failed to clippter sethwnd (hr:0x%x)\n", hr);
lpClipper->Release();
lpPrimary->Release();
pDD->Release();
return -1;
}

hr = lpPrimary->SetClipper(lpClipper);
if (hr != DD_OK)
{
TRACE("failed to set clipper (hr:0x%x)\n", hr);
lpClipper->Release();
lpPrimary->Release();
pDD->Release();
return -1;
}


/* creating yuv420 surface */
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = width;
ddsd.dwHeight = height;

memcpy(&ddsd.ddpfPixelFormat, &g_ddpfFormats[0], sizeof(DDPIXELFORMAT));

if ((hr=pDD->CreateSurface(&ddsd, &lpYUVBuffer, NULL)) != DD_OK)
{
TRACE("failed to create yuv buffer surface\n");
return -1;
}
...
}


< Primary Surface 로 변환해서 출력 - yuv surface -> primary surface blt >

int draw(RECT *rectTarget, AVFrame *pPicture)
{
HRESULT hr;

int w = g_width;
int h = g_height;
...
DDSURFACEDESC2 ddsd;

ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
if ((hr=lpYUVBuffer->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)) == DD_OK)
{
LPBYTE lpSurf = (LPBYTE)ddsd.lpSurface;
LPBYTE ptr = lpSurf;
int t_stride = pPicture->linesize[0];
int i;
for (i=0; i < h; i++) {
memcpy(ptr+i*ddsd.lPitch, pPicture->data[0]+i*t_stride, w);
}
ptr += ddsd.lPitch*h;
for (i=0; i < h/2; i++) {
memcpy(ptr+i*ddsd.lPitch/2, pPicture->data[2]+i*t_stride/2, w/2);
}
ptr += ddsd.lPitch*h/4;
for (i=0; i < h/2; i++) {
memcpy(ptr+i*ddsd.lPitch/2, pPicture->data[1]+i*t_stride/2, w/2);
}

lpYUVBuffer->Unlock(NULL);
}

RECT rectSrc;
rectSrc.top = 0;
rectSrc.left = 0;
rectSrc.right = w;
rectSrc.bottom = h;

hr = lpPrimary->Blt(rectTarget, lpYUVBuffer, &rectSrc, DDBLT_ASYNC, NULL);
}

< ddraw resource release >


void release()
{
if (pDD != NULL)
{
pDD->Release();
pDD = NULL;
}

if (lpPrimary != NULL)
{
lpPrimary->Release();
lpPrimary = NULL;
}

if (lpYUVBuffer != NULL)
{
lpYUVBuffer->Release();
lpYUVBuffer = NULL;
}
}



위 소스에서 AVFrame 은 ffmpeg 에서 사용하는 구조체이고 data[0], data[2], data[1] 는 각각 yuv(yuv420) 값을 가진다.
분리된(non-interleaved) yuv 값을 가진 버퍼를 yuv surface에 memcpy 로 적절하게 복사한 다음 primary surface 로 blt 하면 하드웨어 가속기를 이용한 고속복사를 통해 영상 이미지를 출력할 수 있다.
여기서 primary surface의 픽셀포멧은 RGB32 인데 다른 픽셀포멧의 surface 에다 blt 시켜서 변환시킬수도 있을것같다.