//------------------------------------------------------------------------------ // File: allocpresenter.cpp // // Desc: DirectShow sample code - Custom Allocator Presenter for VMR sample // // Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #include #include #include "project.h" #include #include #include #include "alloclib.h" /******************************Public*Routine******************************\ * NonDelegatingQueryInterface * \**************************************************************************/ STDMETHODIMP CMpegMovie::NonDelegatingQueryInterface( REFIID riid, void** ppv ) { if(riid == IID_IVMRSurfaceAllocator) { return GetInterface((IVMRSurfaceAllocator*)this, ppv); } else if(riid == IID_IVMRImagePresenter) { return GetInterface((IVMRImagePresenter*)this, ppv); } return CUnknown::NonDelegatingQueryInterface(riid,ppv); } ////////////////////////////////////////////////////////////////////////////// // // IVMRSurfaceAllocator // ////////////////////////////////////////////////////////////////////////////// /******************************Public*Routine******************************\ * AllocateSurfaces * \**************************************************************************/ STDMETHODIMP CMpegMovie::AllocateSurface( DWORD_PTR x, VMRALLOCATIONINFO *w, DWORD* lpdwBuffer, LPDIRECTDRAWSURFACE7* lplpSurface ) { DWORD dwFlags = w->dwFlags; LPBITMAPINFOHEADER lpHdr = w->lpHdr; LPDDPIXELFORMAT lpPixFmt = w->lpPixFmt; LPSIZE lpAspectRatio = &w->szAspectRatio; DWORD dwMinBuffers = w->dwMinBuffers; DWORD dwMaxBuffers = w->dwMaxBuffers; if(!lpHdr) { return E_POINTER; } if(!lpAspectRatio) { return E_POINTER; } if(dwFlags & AMAP_PIXELFORMAT_VALID) { if(!lpPixFmt) { return E_INVALIDARG; } } HRESULT hr = AllocateSurfaceWorker(dwFlags, lpHdr, lpPixFmt, lpAspectRatio, dwMinBuffers, dwMaxBuffers, lpdwBuffer, lplpSurface); return hr; } /******************************Public*Routine******************************\ * AllocateSurfaceWorker * \**************************************************************************/ HRESULT CMpegMovie::AllocateSurfaceWorker( DWORD dwFlags, LPBITMAPINFOHEADER lpHdr, LPDDPIXELFORMAT lpPixFmt, LPSIZE lpAspectRatio, DWORD dwMinBuffers, DWORD dwMaxBuffers, DWORD* lpdwBuffer, LPDIRECTDRAWSURFACE7* lplpSurface ) { LPBITMAPINFOHEADER lpHeader = lpHdr; if(!lpHeader) { DbgLog((LOG_ERROR, 1, TEXT("Can't get bitmapinfoheader from media type!!"))); return E_INVALIDARG; } DDSURFACEDESC2 ddsd; INITDDSTRUCT(ddsd); ddsd.dwFlags = DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH | DDSD_PIXELFORMAT; ddsd.dwWidth = abs(lpHeader->biWidth); ddsd.dwHeight = abs(lpHeader->biHeight); ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY /*| DDSCAPS_TEXTURE */; ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); if(lpHdr->biCompression <= BI_BITFIELDS && m_DispInfo.bmiHeader.biBitCount <= lpHdr->biBitCount) { ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; if(lpHdr->biBitCount == 32) { ddsd.ddpfPixelFormat.dwRGBBitCount = 32; ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000; ddsd.ddpfPixelFormat.dwGBitMask = 0xff00; ddsd.ddpfPixelFormat.dwBBitMask = 0xff; } else if(lpHdr->biBitCount == 16) { ddsd.ddpfPixelFormat.dwRGBBitCount = 16; ddsd.ddpfPixelFormat.dwRBitMask = 0xF800; ddsd.ddpfPixelFormat.dwGBitMask = 0x07e0; ddsd.ddpfPixelFormat.dwBBitMask = 0x001F; } } else if(lpHdr->biCompression > BI_BITFIELDS) { const DWORD dwCaps = (DDCAPS_BLTFOURCC | DDCAPS_BLTSTRETCH); if((dwCaps & m_ddHWCaps.dwCaps) != dwCaps) { DbgLog((LOG_ERROR, 1, TEXT("Can't BLT_FOURCC | BLT_STRETCH!!"))); return E_FAIL; } ddsd.ddpfPixelFormat.dwFourCC = lpHdr->biCompression; ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; ddsd.ddpfPixelFormat.dwYUVBitCount = lpHdr->biBitCount; } else { return E_FAIL; } // Adjust width and height, if the driver requires it DWORD dwWidth = ddsd.dwWidth; DWORD dwHeight = ddsd.dwHeight; HRESULT hr = m_lpDDObj->CreateSurface(&ddsd, &m_lpDDTexture, NULL); if(SUCCEEDED(hr)) { m_VideoAR = *lpAspectRatio; m_VideoSize.cx = abs(lpHeader->biWidth); m_VideoSize.cy = abs(lpHeader->biHeight); SetRect(&m_rcDst, 0, 0, m_VideoSize.cx, m_VideoSize.cy); m_rcSrc = m_rcDst; hr = PaintDDrawSurfaceBlack(m_lpDDTexture); *lplpSurface = m_lpDDTexture; *lpdwBuffer = 1; } return hr; } /******************************Public*Routine******************************\ * FreeSurfaces() * \**************************************************************************/ STDMETHODIMP CMpegMovie::FreeSurface(DWORD_PTR w) { if(m_lpDDTexture) { m_lpDDTexture->Release(); m_lpDDTexture = NULL; } return S_OK; } /******************************Public*Routine******************************\ * PrepareSurface * \**************************************************************************/ STDMETHODIMP CMpegMovie::PrepareSurface( DWORD_PTR w, LPDIRECTDRAWSURFACE7 lplpSurface, DWORD dwSurfaceFlags ) { return S_OK; } /******************************Public*Routine******************************\ * AdviseNotify * \**************************************************************************/ STDMETHODIMP CMpegMovie::AdviseNotify( IVMRSurfaceAllocatorNotify* lpIVMRSurfAllocNotify ) { return E_NOTIMPL; } ////////////////////////////////////////////////////////////////////////////// // // IVMRImagePresenter // ////////////////////////////////////////////////////////////////////////////// /******************************Public*Routine******************************\ * StartPresenting() * \**************************************************************************/ STDMETHODIMP CMpegMovie::StartPresenting(DWORD_PTR w) { return S_OK; } /******************************Public*Routine******************************\ * StopPresenting() * \**************************************************************************/ STDMETHODIMP CMpegMovie::StopPresenting(DWORD_PTR w) { return S_OK; } /******************************Public*Routine******************************\ * PresentImage * \**************************************************************************/ STDMETHODIMP CMpegMovie::PresentImage( DWORD_PTR w, VMRPRESENTATIONINFO* p ) { // // Call the app specific function to render the scene // LPDIRECTDRAWSURFACE7 lpSurface = p->lpSurf; const REFERENCE_TIME rtNow = p->rtStart; const DWORD dwSurfaceFlags = p->dwFlags; CAutoLock Lock(&m_AppImageLock); RECT rSrc = {0, 0, m_VideoSize.cx, m_VideoSize.cy}; RECT rDst = {0, 0, WIDTH(&m_rcDst), HEIGHT(&m_rcDst)}; //PaintDDrawSurfaceBlack(m_lpBackBuffer); //m_lpBltAlpha->AlphaBlt(&rDst, m_lpDDTexture, &rSrc, 0xFF); m_lpBackBuffer->Blt(&rDst, m_lpDDTexture, &rSrc, DDBLT_WAIT, NULL); rDst.left = m_cxFontImg; rDst.top = m_cyFontImg * 1; rDst.right = rDst.left + (m_cxFontImg * 40); rDst.bottom = rDst.top + (m_cyFontImg * 4); rSrc.left = 0; rSrc.top = 0; rSrc.right = (m_cxFontImg * 40); rSrc.bottom = (m_cyFontImg * 4); m_lpBltAlpha->AlphaBlt(&rDst, m_lpDDAppImage, &rSrc, 0x80); RECT rc = m_rcDst; RECT rcSrc; SetRect(&rcSrc, 0, 0, WIDTH(&rc), HEIGHT(&rc)); m_lpPriSurf->Blt(&rc, m_lpBackBuffer, &rcSrc, DDBLT_WAIT, NULL); return S_OK; } ////////////////////////////////////////////////////////////////////////////// // // Allocator Presenter helper functions // ////////////////////////////////////////////////////////////////////////////// /*****************************Private*Routine******************************\ * InitDisplayInfo * \**************************************************************************/ BOOL InitDisplayInfo( AMDISPLAYINFO* lpDispInfo ) { static char szDisplay[] = "DISPLAY"; ZeroMemory(lpDispInfo, sizeof(*lpDispInfo)); HDC hdcDisplay = CreateDCA(szDisplay, NULL, NULL, NULL); HBITMAP hbm = CreateCompatibleBitmap(hdcDisplay, 1, 1); lpDispInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); GetDIBits(hdcDisplay, hbm, 0, 1, NULL, (BITMAPINFO *)lpDispInfo, DIB_RGB_COLORS); GetDIBits(hdcDisplay, hbm, 0, 1, NULL, (BITMAPINFO *)lpDispInfo, DIB_RGB_COLORS); DeleteObject(hbm); DeleteDC(hdcDisplay); return TRUE; } /*****************************Private*Routine******************************\ * DDARGB32SurfaceInit * \**************************************************************************/ HRESULT CMpegMovie::DDARGB32SurfaceInit( LPDIRECTDRAWSURFACE7* lplpDDS, BOOL bTexture, DWORD cx, DWORD cy ) { DDSURFACEDESC2 ddsd; HRESULT hRet; *lplpDDS = NULL; INITDDSTRUCT(ddsd); ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; if(bTexture) { ddsd.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS; } ddsd.ddpfPixelFormat.dwRGBBitCount = 32; if(bTexture) { ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000; } ddsd.ddpfPixelFormat.dwRBitMask = 0x00FF0000; ddsd.ddpfPixelFormat.dwGBitMask = 0x0000FF00; ddsd.ddpfPixelFormat.dwBBitMask = 0x000000FF; if(bTexture) { ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE; ddsd.ddsCaps.dwCaps2 = (DDSCAPS2_TEXTUREMANAGE | DDSCAPS2_HINTDYNAMIC); } else { ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OFFSCREENPLAIN; } ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT; ddsd.dwBackBufferCount = 0; if(bTexture) { for(ddsd.dwWidth=1; cx>ddsd.dwWidth; ddsd.dwWidth<<=1); for(ddsd.dwHeight=1; cy>ddsd.dwHeight; ddsd.dwHeight<<=1); } else { ddsd.dwWidth=cx; ddsd.dwHeight=cy; } // Attempt to create the surface with theses settings hRet = m_lpDDObj->CreateSurface(&ddsd, lplpDDS, NULL); return hRet; } /*****************************Private*Routine******************************\ * CreateFontCache * \**************************************************************************/ HRESULT CMpegMovie::CreateFontCache( int cyFont ) { // // Initialize the LOGFONT structure - we want to // create an "anti-aliased" Lucida Consol font // LOGFONT lfChar; ZeroMemory(&lfChar, sizeof(lfChar)); lfChar.lfHeight = -cyFont; lfChar.lfCharSet = OEM_CHARSET ; lfChar.lfPitchAndFamily = FIXED_PITCH | FF_MODERN ; lstrcpy(lfChar.lfFaceName, TEXT("Lucida Console")) ; lfChar.lfWeight = FW_NORMAL ; lfChar.lfOutPrecision = OUT_STRING_PRECIS ; lfChar.lfClipPrecision = CLIP_STROKE_PRECIS ; lfChar.lfQuality = ANTIALIASED_QUALITY; HFONT hFont = CreateFontIndirect(&lfChar) ; if(!hFont) { return E_OUTOFMEMORY; } // // The following magic is necessary to get GDI to rasterize // the font with anti-aliasing switched on when we later use // the font in a DDraw Surface. The doc's say that this is only // necessary in Win9X - but Win2K seems to require it too. // SIZE size; HDC hdcWin = GetDC(NULL); hFont = (HFONT)SelectObject(hdcWin, hFont); GetTextExtentPoint32(hdcWin, TEXT("A"), 1, &size); hFont = (HFONT)SelectObject(hdcWin, hFont); ReleaseDC(NULL, hdcWin); // // Make sure that the font doesn't get too big. // if(size.cx * GRID_CX > 1024) { return S_OK; } // // Delete the old font and assign the new one // RELEASE(m_lpDDSFontCache); if(m_hFont) { DeleteObject(m_hFont); } m_cxFont = size.cx; m_cyFont = size.cy; m_hFont = hFont; // // Create the DDraw ARGB32 surface that we will use // for the font cache. // HRESULT hr = DDARGB32SurfaceInit(&m_lpDDSFontCache, TRUE, 16 * size.cx, 6 * size.cy); if(hr == DD_OK) { HDC hdcDest; m_lpDDSFontCache->GetDC(&hdcDest); // // Select the font into the DDraw surface and draw the characters // m_hFont = (HFONT)SelectObject(hdcDest, m_hFont); SetTextColor(hdcDest, RGB(255,255,255)); SetBkColor(hdcDest, RGB(0,0,0)); SetBkMode(hdcDest, OPAQUE); int row, col; TCHAR ch = (TCHAR)32; for(row = 0; row < 6; row++) { for(col = 0; col < 16; col++) { TextOut(hdcDest, col * size.cx, row * size.cy, &ch, 1); ch++; } } m_hFont = (HFONT)SelectObject(hdcDest, m_hFont); m_lpDDSFontCache->ReleaseDC(hdcDest); DDSURFACEDESC2 surfDesc; INITDDSTRUCT(surfDesc); HRESULT hr = m_lpDDSFontCache->Lock(NULL, &surfDesc, DDLOCK_WAIT, NULL); if(hr == DD_OK) { LPDWORD lpDst = (LPDWORD)surfDesc.lpSurface; for(row = 0; row < 6 * size.cy; row++) { LPDWORD lp = lpDst; for(col = 0; col < 16 * size.cx; col++) { DWORD dwPel = *lp; if(dwPel) { dwPel <<= 24; dwPel |= 0x00FFFFFF; } else { dwPel = 0x80000000; } *lp++ = dwPel; } lpDst += (surfDesc.lPitch / 4); } m_lpDDSFontCache->Unlock(NULL); } } return S_OK; } /*****************************Private*Routine******************************\ * Initialize3DEnvironment * \**************************************************************************/ HRESULT CMpegMovie::Initialize3DEnvironment( HWND hWnd ) { HRESULT hr; // // Create the IDirectDraw interface. The first parameter is the GUID, // which is allowed to be NULL. If there are more than one DirectDraw // drivers on the system, a NULL guid requests the primary driver. For // non-GDI hardware cards like the 3DFX and PowerVR, the guid would need // to be explicity specified . (Note: these guids are normally obtained // from enumeration, which is convered in a subsequent tutorial.) // m_hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY); hr = DirectDrawCreateEx(NULL, (VOID**)&m_lpDDObj, IID_IDirectDraw7, NULL); if(FAILED(hr)) return hr; // // get the h/w caps for this device // INITDDSTRUCT(m_ddHWCaps); hr = m_lpDDObj->GetCaps(&m_ddHWCaps, NULL); if(FAILED(hr)) return hr; InitDisplayInfo(&m_DispInfo); // // Set the Windows cooperative level. This is where we tell the system // whether we will be rendering in fullscreen mode or in a window. Note // that some hardware (non-GDI) may not be able to render into a window. // The flag DDSCL_NORMAL specifies windowed mode. Using fullscreen mode // is the topic of a subsequent tutorial. The DDSCL_FPUSETUP flag is a // hint to DirectX to optomize floating points calculations. See the docs // for more info on this. Note: this call could fail if another application // already controls a fullscreen, exclusive mode. // hr = m_lpDDObj->SetCooperativeLevel(hWnd, DDSCL_NORMAL); if(FAILED(hr)) return hr; // // Initialize a surface description structure for the primary surface. The // primary surface represents the entire display, with dimensions and a // pixel format of the display. Therefore, none of that information needs // to be specified in order to create the primary surface. // DDSURFACEDESC2 ddsd; ZeroMemory(&ddsd, sizeof(DDSURFACEDESC2)); ddsd.dwSize = sizeof(DDSURFACEDESC2); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; // // Create the primary surface. // hr = m_lpDDObj->CreateSurface(&ddsd, &m_lpPriSurf, NULL); if(FAILED(hr)) return hr; // // Create a clipper object which handles all our clipping for cases when // our window is partially obscured by other windows. This is not needed // for apps running in fullscreen mode. // LPDIRECTDRAWCLIPPER pcClipper; hr = m_lpDDObj->CreateClipper(0, &pcClipper, NULL); if(FAILED(hr)) return hr; // // Associate the clipper with our window. Note that, afterwards, the // clipper is internally referenced by the primary surface, so it is safe // to release our local reference to it. // pcClipper->SetHWnd(0, hWnd); m_lpPriSurf->SetClipper(pcClipper); pcClipper->Release(); // // Before creating the device, check that we are NOT in a palettized // display. That case will cause CreateDevice() to fail, since this simple // tutorial does not bother with palettes. // ddsd.dwSize = sizeof(DDSURFACEDESC2); m_lpDDObj->GetDisplayMode(&ddsd); if(ddsd.ddpfPixelFormat.dwRGBBitCount <= 8) return DDERR_INVALIDMODE; DWORD dwRenderWidth = ddsd.dwWidth; DWORD dwRenderHeight = ddsd.dwHeight; // // Setup a surface description to create a backbuffer. This is an // offscreen plain surface with dimensions equal to the current display // size. // The DDSCAPS_3DDEVICE is needed so we can later query this surface // for an IDirect3DDevice interface. // ZeroMemory(&ddsd, sizeof(DDSURFACEDESC2)); ddsd.dwSize = sizeof(DDSURFACEDESC2); ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_3DDEVICE; ddsd.dwWidth = dwRenderWidth; ddsd.dwHeight = dwRenderHeight; // // Create the backbuffer. The most likely reason for failure is running // out of video memory. (A more sophisticated app should handle this.) // hr = m_lpDDObj->CreateSurface(&ddsd, &m_lpBackBuffer, NULL); if(FAILED(hr)) return hr; // // Create the textbuffer. // // The text buffer should be RGB32 (for now - later I'll try // ARGB16:4:4:4:4, but that is a lot more work). // hr = DDARGB32SurfaceInit(&m_lpDDAppImage, TRUE, 1024, MulDiv(4, (int)dwRenderHeight, GRID_CY)); if(FAILED(hr)) return hr; PaintDDrawSurfaceBlack(m_lpDDAppImage); // // Create the device. The device is created off of our back buffer, which // becomes the render target for the newly created device. Note that the // z-buffer must be created BEFORE the device // m_lpBltAlpha = new CAlphaBlt(m_lpBackBuffer, &hr); if(m_lpBltAlpha == NULL || hr != DD_OK) { if(m_lpBltAlpha == NULL) { hr = E_OUTOFMEMORY; } delete m_lpBltAlpha; } hr = CreateFontCache(32); return hr; } POINT LookUpChar(char ch, int cxFont, int cyFont) { ch -= 32; int row = ch / 16; int col = ch % 16; POINT pt; pt.x = col * cxFont; pt.y = row * cyFont; return pt; } /******************************Public*Routine******************************\ * SetAppText * \**************************************************************************/ BOOL CMpegMovie::SetAppText( char* sz ) { DDBLTFX ddFX; INITDDSTRUCT(ddFX); ddFX.dwFillColor = 0xFF80FF80; CAutoLock Lock(&m_AppImageLock); m_lpDDAppImage->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &ddFX); m_cxFontImg = m_cxFont; m_cyFontImg = m_cyFont; RECT rcDst = {0, 0, m_cxFont, m_cyFont}; while(*sz) { if(*sz == '\n') { OffsetRect(&rcDst, 0, m_cyFont); rcDst.left = 0; rcDst.right = m_cxFont; } else { POINT pt = LookUpChar(*sz, m_cxFont, m_cyFont); RECT rcSrc; rcSrc.left = pt.x; rcSrc.top = pt.y; rcSrc.right = pt.x + m_cxFont; rcSrc.bottom = pt.y + m_cyFont; m_lpDDAppImage->Blt(&rcDst, m_lpDDSFontCache, &rcSrc, DDBLT_WAIT, NULL); OffsetRect(&rcDst, m_cxFont, 0); } sz++; } return TRUE; }