//------------------------------------------------------------------------------ // File: Bitmap.cpp // // Desc: DirectShow sample code - Bitmap manipulation routines for // VMR alpha-blended bitmap // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #include #include #include #include #include #include "watermark.h" #include "bitmap.h" #include "resource.h" // // Constants // const float EDGE_BUFFER = 0.04f; // Pixel buffer between bitmap and window edge // (represented in composition space [0 - 1.0f]) const float SLIDE_VALUE = 0.05f; // Amount to slide image in composition space const float STROBE_VALUE = 0.125f; // Amount to add to bitmap alpha value const int ANIMATE_TIMER = 2000; // Timer IDs const int SLIDE_TIMER = 2001; const int STROBE_TIMER = 2002; const int MAIN_TIMER = 2003; const int ANIMATE_TIMEOUT = 250; // Timer delays in milliseconds const int SLIDE_TIMEOUT = 125; const int STROBE_TIMEOUT = 125; const int MAIN_TIMEOUT = 125; const int NUM_IMAGES_IN_BITMAP = 5; // Five images in single wide bitmap (320 x 64) // // Global data // IVMRMixerBitmap9 *pBMP = NULL; DWORD g_dwWatermarkFlags=0; int gnTimer=0; float g_fBitmapCompWidth=0; int g_nBitmapWidth=0; // Width of wide multi-image bitmap int g_nImageWidth=0; // Width of single image in wide multi-image bitmap BOOL g_bRestoreFlip=0, g_bRestoreMirror=0; HBITMAP g_hbmAnimate=0; BOOL g_bWatermarkDisabled=FALSE; // Rectangles used for alpha-blended watermark RECT g_rSrc={0}, g_rSrcSingle={0}; VMR9NormalizedRect g_rDest={0}; HRESULT BlendApplicationImage(HWND hwndApp) { LONG cx, cy; HRESULT hr; RECT rc={0}; // Read the default video size hr = pWC->GetNativeVideoSize(&cx, &cy, NULL, NULL); if (FAILED(hr)) return hr; // Load the multi-image bitmap to alpha blend from the resource file HBITMAP hbm = LoadBitmap(ghInst, MAKEINTRESOURCE(IDR_VMR_WIDE)); BITMAP bm; HBITMAP hbmOld; HDC hdc = GetDC(hwndApp); HDC hdcBmp = CreateCompatibleDC(hdc); ReleaseDC(hwndApp, hdc); GetObject(hbm, sizeof(bm), &bm); hbmOld = (HBITMAP)SelectObject(hdcBmp, hbm); // Configure the VMR's bitmap structure VMR9AlphaBitmap bmpInfo; ZeroMemory(&bmpInfo, sizeof(bmpInfo) ); bmpInfo.dwFlags = VMRBITMAP_HDC; bmpInfo.hdc = hdcBmp; // The wide bitmap contains five similar images that can be // cycled to provide the illusion of animation. g_nBitmapWidth = bm.bmWidth; g_nImageWidth = bm.bmWidth / NUM_IMAGES_IN_BITMAP; // Display the bitmap in the bottom right corner. // rSrc specifies the source rectangle in the GDI device context. // To enable animating between multiple single images within a wide // bitmap, we must specify the entire rectangle of the wide image. // The VMR will convert this rectangle into a DirectDraw surface, // within which we can select a smaller source rectangle to display. SetRect(&rc, 0, 0, g_nBitmapWidth, bm.bmHeight); bmpInfo.rSrc = rc; // rDest specifies the destination rectangle in composition space (0.0f to 1.0f) bmpInfo.rDest.left = (float)(cx - g_nImageWidth) / (float)cx - EDGE_BUFFER; bmpInfo.rDest.top = (float)(cy - bm.bmHeight) / (float)cy - EDGE_BUFFER; bmpInfo.rDest.right = 1.0f - EDGE_BUFFER; bmpInfo.rDest.bottom = 1.0f - EDGE_BUFFER; // Copy initial settings to global memory for later modification g_rDest = bmpInfo.rDest; g_rSrc = bmpInfo.rSrc; // Save the ratio of the bitmap's width to the width of the video file. // This value is used to reposition the bitmap in composition space. g_fBitmapCompWidth = (float)g_nImageWidth / (float)cx; // Transparency value 1.0 is opaque, 0.0 is transparent. // For initially setting the bitmap, we'll make it transparent // because we need to give the VMR the entire wide image. bmpInfo.fAlpha = 0.0; // Set the COLORREF so that the bitmap outline will be transparent SetColorRef(bmpInfo); // Give the bitmap to the VMR. Since the alpha value is 0, nothing will // be displayed yet on the screen, but the VMR will have the information // that it needs to allow us to modify the bitmap. hr = pBMP->SetAlphaBitmap(&bmpInfo); if (FAILED(hr)) Msg(TEXT("SetAlphaBitmap FAILED! Bitmap operations will fail. hr=0x%x\r\n"), hr); // Clean up GDI resources DeleteObject(SelectObject(hdcBmp, hbmOld)); DeleteObject(hbm); DeleteDC(hdcBmp); // If setting the alpha bitmap succeeded, update its parameters if (SUCCEEDED(hr)) { // // Now change the size of the source rectangle to a single // image width. Update the alpha so that the image will be // properly displayed. // SetRect(&rc, 0, 0, g_nImageWidth, bm.bmHeight); bmpInfo.rSrc = rc; // Save the single-image rectangle for later reference during animation g_rSrcSingle = rc; // Set the necessary flags to update the source rectangle bmpInfo.dwFlags = VMRBITMAP_SRCRECT | VMRBITMAP_SRCCOLORKEY; // Transparency value 1.0 is opaque, 0.0 is transparent. bmpInfo.fAlpha = TRANSPARENCY_VALUE; // Update the source rectangle and alpha values of the bitmap. // Now the image will appear properly on the screen. hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); if (FAILED(hr)) Msg(TEXT("UpdateAlphaBitmapParameters FAILED! hr=0x%x\r\n"), hr); } return hr; } void ClearWatermarkState(void) { KillTimer(); // Clear current watermark flags g_dwWatermarkFlags = 0; // Enable all watermark menu items, which may have been // disabled if the watermark was currently disabled EnableWatermarkMenu(TRUE); // Clear menu check marks CheckMenuItem(ghMenu, ID_FLIP, MF_UNCHECKED); CheckMenuItem(ghMenu, ID_MIRROR, MF_UNCHECKED); CheckMenuItem(ghMenu, ID_ANIMATE, MF_UNCHECKED); CheckMenuItem(ghMenu, ID_SLIDE, MF_UNCHECKED); CheckMenuItem(ghMenu, ID_STROBE, MF_UNCHECKED); CheckMenuItem(ghMenu, ID_ALL_EFFECTS, MF_UNCHECKED); CheckMenuItem(ghMenu, ID_NO_EFFECTS, MF_UNCHECKED); } void EnableWatermarkMenu(BOOL bEnable) { EnableMenuItem(ghMenu, ID_FLIP, bEnable ? MF_ENABLED : MF_GRAYED); EnableMenuItem(ghMenu, ID_MIRROR, bEnable ? MF_ENABLED : MF_GRAYED); EnableMenuItem(ghMenu, ID_ANIMATE, bEnable ? MF_ENABLED : MF_GRAYED); EnableMenuItem(ghMenu, ID_SLIDE, bEnable ? MF_ENABLED : MF_GRAYED); EnableMenuItem(ghMenu, ID_STROBE, bEnable ? MF_ENABLED : MF_GRAYED); EnableMenuItem(ghMenu, ID_ALL_EFFECTS, bEnable ? MF_ENABLED : MF_GRAYED); EnableMenuItem(ghMenu, ID_NO_EFFECTS, bEnable ? MF_ENABLED : MF_GRAYED); g_bWatermarkDisabled = !bEnable; } HRESULT MirrorWatermark(DWORD dwFlags) { HRESULT hr; VMR9AlphaBitmap bmpInfo={0}; hr = pBMP->GetAlphaBitmapParameters(&bmpInfo); // Configure the X axis if (dwFlags & MARK_MIRROR) { // Swap left/right coordinates float fLeft = bmpInfo.rDest.left; bmpInfo.rDest.left = bmpInfo.rDest.right; bmpInfo.rDest.right = fLeft; CheckMenuItem(ghMenu, ID_MIRROR, MF_CHECKED); } else { // If we're removing the mirror effect while sliding, then // it's not correct to reset the left/right coordinates to // the default saved global values. Instead, just switch // the left/right values. if (dwFlags & MARK_SLIDE) { // Swap left/right coordinates float fLeft = bmpInfo.rDest.left; bmpInfo.rDest.left = bmpInfo.rDest.right; bmpInfo.rDest.right = fLeft; } else { // We're not sliding, so just reset the left/right // coordinates to their original default values. bmpInfo.rDest.left = g_rDest.left; bmpInfo.rDest.right = g_rDest.right; } CheckMenuItem(ghMenu, ID_MIRROR, MF_UNCHECKED); } // Set the COLORREF so that the bitmap outline will be transparent SetColorRef(bmpInfo); hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); if (FAILED(hr)) Msg(TEXT("UpdateAlphaBitmapParameters FAILED! hr=0x%x\r\n"), hr); return hr; } HRESULT FlipWatermark(DWORD dwFlags) { HRESULT hr; VMR9AlphaBitmap bmpInfo={0}; hr = pBMP->GetAlphaBitmapParameters(&bmpInfo); // Configure the Y axis if (dwFlags & MARK_FLIP) { // Swap left/right coordinates float fTop = bmpInfo.rDest.top; bmpInfo.rDest.top = bmpInfo.rDest.bottom; bmpInfo.rDest.bottom = fTop; CheckMenuItem(ghMenu, ID_FLIP, MF_CHECKED); } else { bmpInfo.rDest.top = g_rDest.top; bmpInfo.rDest.bottom = g_rDest.bottom; CheckMenuItem(ghMenu, ID_FLIP, MF_UNCHECKED); } // Set the COLORREF so that the bitmap outline will be transparent SetColorRef(bmpInfo); hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); if (FAILED(hr)) Msg(TEXT("UpdateAlphaBitmapParameters FAILED! hr=0x%x\r\n"), hr); return hr; } HRESULT DisableWatermark(DWORD dwFlags) { HRESULT hr; VMR9AlphaBitmap bmpInfo={0}; // Since the enable/disable menu will always be enabled, // verify that the VMR Alpha bitmap interface is set. if (!pBMP) return E_NOINTERFACE; // Read the current bitmap settings hr = pBMP->GetAlphaBitmapParameters(&bmpInfo); // If the request is to disable the bitmap, then disable it // and ignore the other flags for now if (dwFlags & MARK_DISABLE) { // Disable other watermark menu items EnableWatermarkMenu(FALSE); // Remember if the flip/mirror states are set for redrawing if (g_dwWatermarkFlags & MARK_FLIP) g_bRestoreFlip = TRUE; if (g_dwWatermarkFlags & MARK_MIRROR) g_bRestoreMirror = TRUE; // If animation is active, reset if (dwFlags & MARK_ANIMATE) { ResetAnimation(); // Read the current bitmap settings again post-reset pBMP->GetAlphaBitmapParameters(&bmpInfo); } // Temporarily disable bitmap display bmpInfo.dwFlags = VMRBITMAP_DISABLE; // Update the bitmap settings hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); if (FAILED(hr)) Msg(TEXT("UpdateAlphaBitmapParameters FAILED to disable watermark! hr=0x%x\r\n"), hr); } else { // Reset the bitmap with default values hr = BlendApplicationImage(ghApp); // Reenable other watermark menu items EnableWatermarkMenu(TRUE); // Preserve the previously-set mirror/flip settings if (g_bRestoreFlip) { FlipWatermark(g_dwWatermarkFlags); g_bRestoreFlip = FALSE; } if (g_bRestoreMirror) { MirrorWatermark(g_dwWatermarkFlags); g_bRestoreMirror = FALSE; } } return hr; } void FlipFlag(DWORD dwFlag) { // If the flag is set, clear it if (g_dwWatermarkFlags & dwFlag) g_dwWatermarkFlags &= ~dwFlag; // Otherwise, set the flag else g_dwWatermarkFlags |= dwFlag; } void AnimateWatermark(DWORD dwFlags) { if (dwFlags & MARK_ANIMATE) { StartTimer(); CheckMenuItem(ghMenu, ID_ANIMATE, MF_CHECKED); } else { CheckMenuItem(ghMenu, ID_ANIMATE, MF_UNCHECKED); ResetAnimation(); } } void SlideWatermark(DWORD dwFlags) { if (dwFlags & MARK_SLIDE) { StartTimer(); CheckMenuItem(ghMenu, ID_SLIDE, MF_CHECKED); } else { CheckMenuItem(ghMenu, ID_SLIDE, MF_UNCHECKED); ResetBitmapPosition(); } } void StrobeWatermark(DWORD dwFlags) { // Start cycling the bitmap's alpha value on a timer if (dwFlags & MARK_STROBE) { StartTimer(); CheckMenuItem(ghMenu, ID_STROBE, MF_CHECKED); } else { CheckMenuItem(ghMenu, ID_STROBE, MF_UNCHECKED); ResetBitmapAlpha(); } } void StartTimer(void) { if (!gnTimer) gnTimer = (int) SetTimer(NULL, MAIN_TIMER, MAIN_TIMEOUT, TimerProc); } void KillTimer(void) { if (gnTimer) { KillTimer(NULL, gnTimer); gnTimer = 0; } } VOID CALLBACK TimerProc( HWND hwnd, // handle to window UINT uMsg, // WM_TIMER message UINT_PTR idEvent, // timer identifier DWORD dwTime // current system time ) { static int nToggle=0; // If the user has disabled the watermark, just exit. if (g_bWatermarkDisabled) return; // Use one timer for efficiency, instead of starting/stopping // three separate timers. Since animation will run at half the // rate of slide/strobe, only process animation in every other callback. if (g_dwWatermarkFlags & MARK_SLIDE) HandleSlide(); if (g_dwWatermarkFlags & MARK_STROBE) HandleStrobe(); if (g_dwWatermarkFlags & MARK_ANIMATE) { nToggle ^= 1; if (nToggle) HandleAnimation(); } } void HandleStrobe(void) { HRESULT hr; VMR9AlphaBitmap bmpInfo={0}; hr = pBMP->GetAlphaBitmapParameters(&bmpInfo); // Slowly increase the alpha value float fAlpha = bmpInfo.fAlpha + STROBE_VALUE; if (fAlpha > 1.0f) fAlpha = 0.0f; bmpInfo.fAlpha = fAlpha; // Set the COLORREF so that the bitmap outline will be transparent SetColorRef(bmpInfo); // If the bitmap is currently disabled, this call will fail hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); } void ResetBitmapAlpha(void) { HRESULT hr; VMR9AlphaBitmap bmpInfo={0}; if (!pBMP) return; hr = pBMP->GetAlphaBitmapParameters(&bmpInfo); // Return bitmap alpha to its default value bmpInfo.fAlpha = TRANSPARENCY_VALUE; // Set the COLORREF so that the bitmap outline will be transparent SetColorRef(bmpInfo); hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); if (FAILED(hr)) Msg(TEXT("UpdateAlphaBitmapParameters FAILED! hr=0x%x\r\n"), hr); } void HandleSlide(void) { HRESULT hr; VMR9AlphaBitmap bmpInfo={0}; hr = pBMP->GetAlphaBitmapParameters(&bmpInfo); // Slowly decrease the X coordinate bmpInfo.rDest.left -= SLIDE_VALUE; bmpInfo.rDest.right -= SLIDE_VALUE; // Once the bitmap disappears off the left side of the screen, // reset to the rightmost side of the window. // Take into account that the bitmap might be mirrored, in which case // the left/right coordinates are switched. if ((g_dwWatermarkFlags & MARK_MIRROR) == 0) { // NOT mirrored if (bmpInfo.rDest.right <= EDGE_BUFFER) { bmpInfo.rDest.left = 1.0f; bmpInfo.rDest.right = 1.0f + g_fBitmapCompWidth; } } else { // Mirrored if (bmpInfo.rDest.left <= EDGE_BUFFER) { bmpInfo.rDest.right = 1.0f; bmpInfo.rDest.left = 1.0f + g_fBitmapCompWidth; } } // Set the COLORREF so that the bitmap outline will be transparent SetColorRef(bmpInfo); // If the bitmap is currently disabled, this call will fail hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); } void ResetBitmapPosition(void) { HRESULT hr; VMR9AlphaBitmap bmpInfo={0}; if (!pBMP) return; hr = pBMP->GetAlphaBitmapParameters(&bmpInfo); // Return bitmap position to its original value. // Since the image may be currently mirrored, swap the // left/right coordinates if necessary if (g_dwWatermarkFlags & MARK_MIRROR) { // Mirrored bmpInfo.rDest.left = g_rDest.right; bmpInfo.rDest.right = g_rDest.left; } else { // NOT mirrored bmpInfo.rDest.left = g_rDest.left; bmpInfo.rDest.right = g_rDest.right; } // Set the COLORREF so that the bitmap outline will be transparent SetColorRef(bmpInfo); hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); if (FAILED(hr)) Msg(TEXT("UpdateAlphaBitmapParameters FAILED! hr=0x%x\r\n"), hr); } void HandleAnimation(void) { HRESULT hr; VMR9AlphaBitmap bmpInfo={0}; static int nCycle=0; // Fill the rDest and fAlpha values in bmpInfo hr = pBMP->GetAlphaBitmapParameters(&bmpInfo); // Move the image source to the right by one image width bmpInfo.rSrc.left = g_nImageWidth * nCycle; bmpInfo.rSrc.right = bmpInfo.rSrc.left + g_nImageWidth; bmpInfo.rSrc.top = g_rSrc.top; bmpInfo.rSrc.bottom = g_rSrc.bottom; nCycle++; // If we have passed the last image in the wide bitmap, // then reset to the default source location (leftmost image) if (bmpInfo.rSrc.left >= g_nBitmapWidth) { bmpInfo.rSrc = g_rSrcSingle; nCycle = 1; } // Set the necessary flag to update the source rectangle bmpInfo.dwFlags = VMRBITMAP_SRCRECT; // Set the COLORREF so that the bitmap outline will be transparent SetColorRef(bmpInfo); // Give the VMR a new bitmap to display // If the bitmap is currently disabled, this call will fail hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); } void ResetAnimation(void) { HRESULT hr; VMR9AlphaBitmap bmpInfo={0}; if (!pBMP) return; hr = pBMP->GetAlphaBitmapParameters(&bmpInfo); // Update the source rectangle to the original single-image default bmpInfo.rSrc = g_rSrcSingle; // Set the necessary flag to update the source rectangle bmpInfo.dwFlags = VMRBITMAP_SRCRECT; // Set the COLORREF so that the bitmap outline will be transparent SetColorRef(bmpInfo); hr = pBMP->UpdateAlphaBitmapParameters(&bmpInfo); if (FAILED(hr)) Msg(TEXT("UpdateAlphaBitmapParameters FAILED! hr=0x%x\r\n"), hr); } void SetColorRef(VMR9AlphaBitmap& bmpInfo) { // Set the COLORREF so that the bitmap outline will be transparent bmpInfo.clrSrcKey = RGB(255, 255, 255); // Pure white bmpInfo.dwFlags |= VMRBITMAP_SRCCOLORKEY; } void SetAllEffects(void) { // Activate any effects that aren't currently active if ((g_dwWatermarkFlags & MARK_ANIMATE) == 0) { FlipFlag(MARK_ANIMATE); AnimateWatermark(g_dwWatermarkFlags); } if ((g_dwWatermarkFlags & MARK_STROBE) == 0) { FlipFlag(MARK_STROBE); StrobeWatermark(g_dwWatermarkFlags); } if ((g_dwWatermarkFlags & MARK_SLIDE) == 0) { FlipFlag(MARK_SLIDE); SlideWatermark(g_dwWatermarkFlags); } } void ClearAllEffects(void) { KillTimer(); // Deactivate all active effects if (g_dwWatermarkFlags & MARK_ANIMATE) { FlipFlag(MARK_ANIMATE); AnimateWatermark(g_dwWatermarkFlags); } if (g_dwWatermarkFlags & MARK_STROBE) { FlipFlag(MARK_STROBE); StrobeWatermark(g_dwWatermarkFlags); } if (g_dwWatermarkFlags & MARK_SLIDE) { FlipFlag(MARK_SLIDE); SlideWatermark(g_dwWatermarkFlags); } }