天天看點

DirectDraw 顯示 YUV

在網上找了一段代碼,能工作,但是顔色不對,紅的變成藍色的,黃的變青色了,有時間找找問題。

這個問題在我初學DirectX是困惑了我很久,貼出來為初學者提供一個參考。

#include "ddraw.h"

#pragma comment(lib,"ddraw.lib")

#define FILE_HEIGHT            288

#define FILE_WIDTH            352

#define DRAW_TOP            0

#define DRAW_LEFT            0

#define DRAW_HEIGHT            288

#define DRAW_WIDHT            352

BOOL DrawYV12(HWND hWnd)

{

    LPDIRECTDRAW            lpDD;                // DirectDraw 對象指針

    LPDIRECTDRAWSURFACE     lpDDSPrimary;        // DirectDraw 主表面指針

    LPDIRECTDRAWSURFACE     lpDDSOffScr;        // DirectDraw 離屏表面指針

    DDSURFACEDESC            ddsd;                // DirectDraw 表面描述

    RECT                    rctDest;            // 目标區域

    RECT                    rctSour;            // 源區域

    HRESULT                    ddRval;                // DirectDraw 函數傳回值

    // 建立DirectCraw對象

    if (DirectDrawCreate(NULL, &lpDD, NULL) != DD_OK) 

        return FALSE;

    // 設定協作層

    if (lpDD->SetCooperativeLevel(hWnd,

            DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES) != DD_OK)

    // 建立主表面

    ZeroMemory(&ddsd, sizeof(ddsd));

    ddsd.dwSize = sizeof(ddsd);

    ddsd.dwFlags = DDSD_CAPS;

    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

    if (lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL) != DD_OK)

    // 建立離屏表面對象

    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_OVERLAY

DDSCAPS_OFFSCREENPLAIN;

    ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;

    ddsd.dwWidth = DRAW_WIDHT;

    ddsd.dwHeight = DRAW_HEIGHT;

    ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);

    ddsd.ddpfPixelFormat.dwFlags  = DDPF_FOURCC | DDPF_YUV ;

    ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y','V','1','2');

    ddsd.ddpfPixelFormat.dwYUVBitCount = 8;

    if (lpDD->CreateSurface(&ddsd, &lpDDSOffScr, NULL) != DD_OK)

    // 加載yv12圖像檔案

    FILE * f = fopen("test.yv12","rb");

    LPBYTE lpYV12 = new BYTE[FILE_WIDTH * FILE_HEIGHT * 3 / 2];

    UINT iLen = fread(lpYV12, 1, FILE_WIDTH * FILE_HEIGHT * 3 / 2, f);

    fclose(f);

    LPBYTE lpY = lpYV12;

    LPBYTE lpV = lpYV12 + FILE_WIDTH * FILE_HEIGHT;

    LPBYTE lpU = lpYV12 + FILE_WIDTH * FILE_HEIGHT * 5 / 4;

    ddRval = lpDDSOffScr->Lock(NULL,&ddsd,DDLOCK_WAIT | DDLOCK_WRITEONLY,NULL);

    while(ddRval == DDERR_WASSTILLDRAWING);

    if(ddRval != DD_OK)

    LPBYTE lpSurf = (LPBYTE)ddsd.lpSurface;

    LPBYTE lpY1 = lpSurf;

    LPBYTE lpV1 = lpSurf + ddsd.lPitch * FILE_HEIGHT;

    LPBYTE lpU1 = lpV1 + ddsd.lPitch  * FILE_HEIGHT / 4;

    int nOffset = DRAW_TOP*FILE_WIDTH+DRAW_LEFT;

    // 填充離屏表面

    if(lpSurf)

    {

        int i = 0;

        // fill Y data

        lpY += nOffset;

        for(i=0; i<ddsd.dwHeight; i++)

        {

            memcpy(lpSurf, lpY, ddsd.dwWidth);

            lpY += FILE_WIDTH;

            lpSurf += ddsd.lPitch;

        }

        // fill V data

        lpV += DRAW_TOP * FILE_WIDTH / 4 + DRAW_LEFT / 2;

        for(i=0; i<ddsd.dwHeight/2; i++)

            memcpy(lpSurf, lpV, ddsd.dwWidth / 2);

            lpV += FILE_WIDTH / 2;

            lpSurf += ddsd.lPitch / 2;

        // fill U data

        lpU += DRAW_TOP * FILE_WIDTH / 4 + DRAW_LEFT / 2;

            memcpy(lpSurf, lpU, ddsd.dwWidth / 2);

            lpU += FILE_WIDTH / 2;

    }

    lpDDSOffScr->Unlock(NULL);

    delete lpYV12;

    // Blt到主表面上

    rctSour.left = 0;

    rctSour.top = 0;

    rctSour.right = ddsd.dwWidth;

    rctSour.bottom = ddsd.dwHeight;

    GetClientRect(hWnd,&rctDest);

    ClientToScreen(hWnd, (LPPOINT)&rctDest.left);

    ClientToScreen(hWnd, (LPPOINT)&rctDest.right);

    ddRval = lpDDSPrimary->Blt(&rctDest, lpDDSOffScr, &rctSour, DDBLT_WAIT, NULL);

    // 釋放DirectDraw對象

    if(lpDD != NULL)

        if(lpDDSPrimary != NULL)

            lpDDSPrimary->Release();

            lpDDSPrimary = NULL;

        if(lpDDSOffScr != NULL)

            lpDDSOffScr->Release();

            lpDDSOffScr = NULL;

        lpDD->Release();

        lpDD = NULL;

    return TRUE;

}