2025-11-28 00:35:46 +09:00

699 lines
19 KiB
C++

//------------------------------------------------------------------------------
// File: Bitmap.cpp
//
// Desc: DirectShow sample code - Bitmap manipulation routines for
// VMR alpha-blended bitmap
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
#include <tchar.h>
#include <dshow.h>
#include <commctrl.h>
#include <commdlg.h>
#include <stdio.h>
#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);
}
}