WINUTIL.CPP
//==========================================================================; 
// 
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR 
//  PURPOSE. 
// 
//  Copyright (c) 1992 - 1997  Microsoft Corporation.  All Rights Reserved. 
// 
//--------------------------------------------------------------------------; 
 
// Generic window handler base class, December 1995 
 
#include <streams.h> 
#include <limits.h> 
 
static UINT MsgDestroy; 
 
// Constructor 
 
CBaseWindow::CBaseWindow(BOOL bDoGetDC) : 
    m_hInstance(g_hInst), 
    m_hwnd(NULL), 
    m_hdc(NULL), 
    m_bActivated(FALSE), 
    m_pClassName(NULL), 
    m_ClassStyles(0), 
    m_WindowStyles(0), 
    m_WindowStylesEx(0), 
    m_ShowStageMessage(0), 
    m_ShowStageTop(0), 
    m_MemoryDC(NULL), 
    m_hPalette(NULL), 
    m_bBackground(FALSE), 
#ifdef DEBUG 
    m_bRealizing(FALSE), 
#endif 
    m_bNoRealize(FALSE) 
{ 
    m_bDoGetDC = bDoGetDC; 
} 
 
 
// Prepare a window by spinning off a worker thread to do the creation and 
// also poll the message input queue. We leave this to be called by derived 
// classes because they might want to override methods like MessageLoop and 
// InitialiseWindow, if we do this during construction they'll ALWAYS call 
// this base class methods. We make the worker thread create the window so 
// it owns it rather than the filter graph thread which is constructing us 
 
HRESULT CBaseWindow::PrepareWindow() 
{ 
    if (m_hwnd) return NOERROR; 
    ASSERT(m_hwnd == NULL); 
    ASSERT(m_hdc == NULL); 
 
    // Get the derived object's window and class styles 
 
    m_pClassName = GetClassWindowStyles(&m_ClassStyles, 
                                        &m_WindowStyles, 
                                        &m_WindowStylesEx); 
    if (m_pClassName == NULL) { 
        return E_FAIL; 
    } 
 
    // Register our special private messages 
    m_ShowStageMessage = RegisterWindowMessage(SHOWSTAGE); 
    m_ShowStageTop = RegisterWindowMessage(SHOWSTAGETOP); 
    m_RealizePalette = RegisterWindowMessage(REALIZEPALETTE); 
 
    return DoCreateWindow(); 
} 
 
 
// Destructor just a placeholder so that we know it becomes virtual 
// Derived classes MUST call DoneWithWindow in their destructors so 
// that no messages arrive after the derived class constructor ends 
 
#ifdef DEBUG 
CBaseWindow::~CBaseWindow() 
{ 
    ASSERT(m_hwnd == NULL); 
    ASSERT(m_hdc == NULL); 
} 
#endif 
 
 
// We use the sync worker event to have the window destroyed. All we do is 
// signal the event and wait on the window thread handle. Trying to send it 
// messages causes too many problems, furthermore to be on the safe side we 
// just wait on the thread handle while it returns WAIT_TIMEOUT or there is 
// a sent message to process on this thread. If the constructor failed to 
// create the thread in the first place then the loop will get terminated 
 
HRESULT CBaseWindow::DoneWithWindow() 
{ 
    if (GetWindowThreadProcessId(m_hwnd, NULL) != GetCurrentThreadId()) { 
        MsgDestroy = RegisterWindowMessage(TEXT("AM_DESTROY")); 
        SendMessage(m_hwnd, MsgDestroy, 0, 0); 
        return NOERROR; 
    } 
    const HWND hwnd = m_hwnd; 
    if (hwnd == NULL) { 
        return NOERROR; 
    } 
 
    InactivateWindow(); 
    NOTE("Inactivated"); 
 
    // Reset the window styles before destruction 
 
    SetWindowLong(hwnd,GWL_STYLE,m_WindowStyles); 
    ASSERT(GetParent(hwnd) == NULL); 
    NOTE1("Reset window styles %d",m_WindowStyles); 
 
    //  UnintialiseWindow sets m_hwnd to NULL so save a copy 
    UninitialiseWindow(); 
    DbgLog((LOG_TRACE, 2, TEXT("Destroying 0x%8.8X"), hwnd)); 
    if (!DestroyWindow(hwnd)) { 
        DbgLog((LOG_TRACE, 0, TEXT("DestroyWindow %8.8X failed code %d"), 
                hwnd, GetLastError())); 
        DbgBreak(""); 
    } 
 
    // Reset our state so we can be prepared again 
 
    m_pClassName = NULL; 
    m_ClassStyles = 0; 
    m_WindowStyles = 0; 
    m_WindowStylesEx = 0; 
    m_ShowStageMessage = 0; 
    m_ShowStageTop = 0; 
 
    return NOERROR; 
} 
 
 
// Called at the end to put the window in an inactive state. The pending list 
// will always have been cleared by this time so event if the worker thread 
// gets has been signaled and gets in to render something it will find both 
// the state has been changed and that there are no available sample images 
// Since we wait on the window thread to complete we don't lock the object 
 
HRESULT CBaseWindow::InactivateWindow() 
{ 
    // Has the window been activated 
    if (m_bActivated == FALSE) { 
        return S_FALSE; 
    } 
 
    m_bActivated = FALSE; 
    ShowWindow(m_hwnd,SW_HIDE); 
    return NOERROR; 
} 
 
 
// This displays a normal window. We ask the base window class for default 
// sizes which unless overriden will return DEFWIDTH and DEFHEIGHT. We go 
// through a couple of extra hoops to get the client area the right size 
// as the object specifies which accounts for the AdjustWindowRectEx calls 
// We also DWORD align the left and top coordinates of the window here to 
// maximise the chance of being able to use DCI/DirectDraw primary surface 
 
HRESULT CBaseWindow::ActivateWindow() 
{ 
    // Has the window been sized and positioned already 
 
    if (m_bActivated == TRUE || GetParent(m_hwnd) != NULL) { 
 
        SetWindowPos(m_hwnd,            // Our window handle 
                     HWND_TOP,          // Put it at the top 
                     0, 0, 0, 0,        // Leave in current position 
                     SWP_NOMOVE |       // Don't change it's place 
                     SWP_NOSIZE);       // Change Z-order only 
 
        return S_FALSE; 
    } 
 
    // Calculate the desired client rectangle 
 
    RECT WindowRect, ClientRect = GetDefaultRect(); 
    GetWindowRect(m_hwnd,&WindowRect); 
    AdjustWindowRectEx(&ClientRect,GetWindowLong(m_hwnd,GWL_STYLE), 
                       FALSE,GetWindowLong(m_hwnd,GWL_EXSTYLE)); 
 
    // Align left and top edges on DWORD boundaries 
 
    UINT WindowFlags = (SWP_NOACTIVATE | SWP_FRAMECHANGED); 
    WindowRect.left -= (WindowRect.left & 3); 
    WindowRect.top -= (WindowRect.top & 3); 
 
    SetWindowPos(m_hwnd,                // Window handle 
                 HWND_TOP,              // Put it at the top 
                 WindowRect.left,       // Align left edge 
                 WindowRect.top,        // And also top place 
                 WIDTH(&ClientRect),    // Horizontal size 
                 HEIGHT(&ClientRect),   // Vertical size 
                 WindowFlags);          // Don't show window 
 
    m_bActivated = TRUE; 
    return NOERROR; 
} 
 
 
// This can be used to DWORD align the window for maximum performance 
 
HRESULT CBaseWindow::PerformanceAlignWindow() 
{ 
    RECT ClientRect,WindowRect; 
    GetWindowRect(m_hwnd,&WindowRect); 
    ASSERT(m_bActivated == TRUE); 
 
    // Don't do this if we're owned 
 
    if (GetParent(m_hwnd)) { 
        return NOERROR; 
    } 
 
    // Align left and top edges on DWORD boundaries 
 
    GetClientRect(m_hwnd, &ClientRect); 
    MapWindowPoints(m_hwnd, HWND_DESKTOP, (LPPOINT) &ClientRect, 2); 
    WindowRect.left -= (ClientRect.left & 3); 
    WindowRect.top  -= (ClientRect.top  & 3); 
    UINT WindowFlags = (SWP_NOACTIVATE | SWP_NOSIZE); 
 
    SetWindowPos(m_hwnd,                // Window handle 
                 HWND_TOP,              // Put it at the top 
                 WindowRect.left,       // Align left edge 
                 WindowRect.top,        // And also top place 
                 (int) 0,(int) 0,       // Ignore these sizes 
                 WindowFlags);          // Don't show window 
 
    return NOERROR; 
} 
 
 
// Install a palette into the base window - we may be called by a different 
// thread to the one that owns the window. We have to be careful how we do 
// the palette realisation as we could be a different thread to the window 
// which would cause an inter thread send message. Therefore we realise the 
// palette by sending it a special message but without the window locked 
 
HRESULT CBaseWindow::SetPalette(HPALETTE hPalette) 
{ 
    // We must own the window lock during the change 
    { 
        CAutoLock cWindowLock(&m_WindowLock); 
        ASSERT(hPalette); 
        m_hPalette = hPalette; 
    } 
    return SetPalette(); 
} 
 
HRESULT CBaseWindow::SetPalette() 
{ 
    if (!m_bNoRealize) { 
        SendMessage(m_hwnd, m_RealizePalette, 0, 0); 
        // Make sure the device's palette is flushed 
        return (GdiFlush() == FALSE ? S_FALSE : S_OK); 
    } else { 
        // Just select the palette 
        ASSERT(m_hdc); 
        ASSERT(m_MemoryDC); 
        SelectPalette(m_hdc,m_hPalette,m_bBackground); 
        SelectPalette(m_MemoryDC,m_hPalette,m_bBackground); 
        return S_OK; 
    } 
} 
 
// Realise our palettes in the window and device contexts 
 
HRESULT CBaseWindow::DoRealisePalette(BOOL bForceBackground) 
{ 
    //  If we grab a critical section here we can deadlock 
    //  with the window thread because one of the side effects 
    //  of RealizePalette is to send a WM_PALETTECHANGED message 
    //  to every window in the system.  In our handling 
    //  of WM_PALETTECHANGED we used to grab this CS too. 
    //  The really bad case is when our renderer calls DoRealisePalette() 
    //  while we're in the middle of processing a palette change 
    //  for another window. 
    //  So don't hold the critical section while actually realising 
    //  the palette.  In any case USER is meant to manage palette 
    //  handling - we shouldn't have to serialize everything as well 
    if (m_hPalette == NULL) { 
 
        return NOERROR; 
    } 
 
    // Realize the palette on the window thread 
    ASSERT(m_hdc); 
    ASSERT(m_MemoryDC); 
    SelectPalette(m_hdc,m_hPalette,m_bBackground || bForceBackground); 
    EXECUTE_ASSERT(RealizePalette(m_hdc) != GDI_ERROR); 
    SelectPalette(m_MemoryDC,m_hPalette,m_bBackground); 
    EXECUTE_ASSERT(RealizePalette(m_MemoryDC) != GDI_ERROR); 
 
    return (GdiFlush() == FALSE ? S_FALSE : S_OK); 
} 
 
 
// This is the global window procedure 
 
LRESULT CALLBACK WndProc(HWND hwnd,         // Window handle 
                         UINT uMsg,         // Message ID 
                         WPARAM wParam,     // First parameter 
                         LPARAM lParam)     // Other parameter 
{ 
 
    // Get the window long that holds our window object pointer 
    // If it is NULL then we are initialising the window in which 
    // case the object pointer has been passed in the window creation 
    // structure.  IF we get any messages before WM_NCCREATE we will 
    // pass them to DefWindowProc. 
 
    CBaseWindow *pBaseWindow = (CBaseWindow *)GetWindowLong(hwnd,0); 
    if (pBaseWindow == NULL) { 
 
// Get the structure pointer from the create struct. 
// We can only do this for WM_NCCREATE which should be one of 
// the first messages we receive.  Anything before this will 
// have to be passed to DefWindowProc (i.e. WM_GETMINMAXINFO) 
 
// If the message is WM_NCCREATE we set our pBaseWindow pointer 
// and will then place it in the window structure 
 
if ((uMsg != WM_NCCREATE) 
    || (NULL == (pBaseWindow = *(CBaseWindow**) ((LPCREATESTRUCT)lParam)->lpCreateParams))) 
{ 
    return(DefWindowProc(hwnd, uMsg, wParam, lParam)); 
} 
 
        // Set the window LONG to be the object who created us 
#ifdef DEBUG 
SetLastError(0);  // because of the way SetWindowLong works 
#endif 
        BOOL rc = SetWindowLong(hwnd, (DWORD) 0, (LONG) pBaseWindow); 
#ifdef DEBUG 
if (0 == rc) { 
    // SetWindowLong MIGHT have failed.  (Read the docs which admit 
    // that it is awkward to work out if you have had an error.) 
    LONG lasterror = GetLastError(); 
    ASSERT(0 == lasterror); 
    // If this is not the case we have not set the pBaseWindow pointer 
    // into the window structure and we will blow up. 
} 
#endif 
 
    } 
    // See if this is the packet of death 
    if (uMsg == MsgDestroy && uMsg != 0) { 
        pBaseWindow->DoneWithWindow(); 
        return 0; 
    } 
    return pBaseWindow->OnReceiveMessage(hwnd,uMsg,wParam,lParam); 
} 
 
 
// When the window size changes we adjust our member variables that 
// contain the dimensions of the client rectangle for our window so 
// that we come to render an image we will know whether to stretch 
 
BOOL CBaseWindow::OnSize(LONG Width, LONG Height) 
{ 
    m_Width = Width; 
    m_Height = Height; 
    return TRUE; 
} 
 
 
// This function handles the WM_CLOSE message 
 
BOOL CBaseWindow::OnClose() 
{ 
    ShowWindow(m_hwnd,SW_HIDE); 
    return TRUE; 
} 
 
 
// This is called by the worker window thread when it receives a terminate 
// message from the window object destructor to delete all the resources we 
// allocated during initialisation. By the time the worker thread exits all 
// processing will have been completed as the source filter disconnection 
// flushes the image pending sample, therefore the GdiFlush should succeed 
 
HRESULT CBaseWindow::UninitialiseWindow() 
{ 
    // Have we already cleaned up 
 
    if (m_hwnd == NULL) { 
        ASSERT(m_hdc == NULL); 
        ASSERT(m_MemoryDC == NULL); 
        return NOERROR; 
    } 
 
    // Release the window resources 
 
    EXECUTE_ASSERT(GdiFlush()); 
 
    if (m_hdc) 
    { 
        EXECUTE_ASSERT(ReleaseDC(m_hwnd,m_hdc)); 
        m_hdc = NULL; 
    } 
 
    if (m_MemoryDC) 
    { 
        EXECUTE_ASSERT(DeleteDC(m_MemoryDC)); 
        m_MemoryDC = NULL; 
    } 
 
    // Reset the window variables 
    m_hwnd = NULL; 
 
    return NOERROR; 
} 
 
 
// This is called by the worker window thread after it has created the main 
// window and it wants to initialise the rest of the owner objects window 
// variables such as the device contexts. We execute this function with the 
// critical section still locked. Nothing in this function must generate any 
// SendMessage calls to the window because this is executing on the window 
// thread so the message will never be processed and we will deadlock 
 
HRESULT CBaseWindow::InitialiseWindow(HWND hwnd) 
{ 
    // Initialise the window variables 
 
    ASSERT(IsWindow(hwnd)); 
    m_hwnd = hwnd; 
 
    if (m_bDoGetDC) 
    { 
        EXECUTE_ASSERT(m_hdc = GetDC(hwnd)); 
        EXECUTE_ASSERT(m_MemoryDC = CreateCompatibleDC(m_hdc)); 
 
        EXECUTE_ASSERT(SetStretchBltMode(m_hdc,COLORONCOLOR)); 
        EXECUTE_ASSERT(SetStretchBltMode(m_MemoryDC,COLORONCOLOR)); 
    } 
 
    return NOERROR; 
} 
 
HRESULT CBaseWindow::DoCreateWindow() 
{ 
    WNDCLASS wndclass;                  // Used to register classes 
    BOOL bRegistered;                   // Is this class registered 
    HWND hwnd;                          // Handle to our window 
 
    bRegistered = GetClassInfo(m_hInstance,   // Module instance 
                               m_pClassName,  // Window class 
                               &wndclass);                 // Info structure 
 
    // if the window is to be used for drawing puposes and we are getting a DC 
    // for the entire lifetime of the window then changes the class style to do 
    // say so. If we don't set this flag then the DC comes from the cache and is 
    // really bad. 
    if (m_bDoGetDC) 
    { 
        m_ClassStyles |= CS_OWNDC; 
    } 
 
    if (bRegistered == FALSE) { 
 
        // Register the renderer window class 
 
        wndclass.lpszClassName = m_pClassName; 
        wndclass.style         = m_ClassStyles; 
        wndclass.lpfnWndProc   = (WNDPROC) WndProc; 
        wndclass.cbClsExtra    = 0; 
        wndclass.cbWndExtra    = sizeof(CBaseWindow *); 
        wndclass.hInstance     = m_hInstance; 
        wndclass.hIcon         = NULL; 
        wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW); 
        wndclass.hbrBackground = (HBRUSH) NULL; 
        wndclass.lpszMenuName  = NULL; 
 
        RegisterClass(&wndclass); 
    } 
 
    // Create the frame window.  Pass the pBaseWindow information in the 
    // CreateStruct which allows our message handling loop to get hold of 
    // the pBaseWindow pointer. 
 
    CBaseWindow *pBaseWindow = this;           // The owner window object 
    hwnd = CreateWindowEx(m_WindowStylesEx,  // Extended styles 
                          m_pClassName,      // Registered name 
                          TEXT("ActiveMovie Window"),     // Window title 
                          m_WindowStyles,    // Window styles 
                          CW_USEDEFAULT,                  // Start x position 
                          CW_USEDEFAULT,                  // Start y position 
                          DEFWIDTH,                       // Window width 
                          DEFHEIGHT,                      // Window height 
                          NULL,                           // Parent handle 
                          NULL,                           // Menu handle 
                          m_hInstance,       // Instance handle 
                          &pBaseWindow);                  // Creation data 
 
    // If we failed signal an error to the object constructor (based on the 
    // last Win32 error on this thread) then signal the constructor thread 
    // to continue, release the mutex to let others have a go and exit 
 
    if (hwnd == NULL) { 
        DWORD Error = GetLastError(); 
        return HRESULT_FROM_WIN32(Error); 
    } 
 
    // Check the window LONG is the object who created us 
    ASSERT(GetWindowLong(hwnd, 0) == (LONG)this); 
 
    // Initialise the window and then signal the constructor so that it can 
    // continue and then finally unlock the object's critical section. The 
    // window class is left registered even after we terminate the thread 
    // as we don't know when the last window has been closed. So we allow 
    // the operating system to free the class resources as appropriate 
 
    InitialiseWindow(hwnd); 
 
    DbgLog((LOG_TRACE, 2, TEXT("Created window class (%s) HWND(%8.8X)"), 
            m_pClassName, hwnd)); 
 
    return S_OK; 
} 
 
 
// The base class provides some default handling and calls DefWindowProc 
 
LRESULT CBaseWindow::OnReceiveMessage(HWND hwnd,         // Window handle 
                                      UINT uMsg,         // Message ID 
                                      WPARAM wParam,     // First parameter 
                                      LPARAM lParam)     // Other parameter 
{ 
    ASSERT(IsWindow(hwnd)); 
 
    if (PossiblyEatMessage(uMsg, wParam, lParam)) 
return 0; 
 
    // This is sent by the IVideoWindow SetWindowForeground method. If the 
    // window is invisible we will show it and make it topmost without the 
    // foreground focus. If the window is visible it will also be made the 
    // topmost window without the foreground focus. If wParam is TRUE then 
    // for both cases the window will be forced into the foreground focus 
 
    if (uMsg == m_ShowStageMessage) { 
 
        BOOL bVisible = IsWindowVisible(hwnd); 
        SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, 
                     SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | 
                     (bVisible ? SWP_NOACTIVATE : 0)); 
 
        // Should we bring the window to the foreground 
        if (wParam == TRUE) { 
            SetForegroundWindow(hwnd); 
        } 
        return (LRESULT) 1; 
    } 
 
    // When we go fullscreen we have to add the WS_EX_TOPMOST style to the 
    // video window so that it comes out above any task bar (this is more 
    // relevant to WindowsNT than Windows95). However the SetWindowPos call 
    // must be on the same thread as that which created the window. The 
    // wParam parameter can be TRUE or FALSE to set and reset the topmost 
 
    if (uMsg == m_ShowStageTop) { 
        HWND HwndTop = (wParam == TRUE ? HWND_TOPMOST : HWND_NOTOPMOST); 
        BOOL bVisible = IsWindowVisible(hwnd); 
        SetWindowPos(hwnd, HwndTop, 0, 0, 0, 0, 
                     SWP_NOMOVE | SWP_NOSIZE | 
                     (wParam == TRUE ? SWP_SHOWWINDOW : 0) | 
                     (bVisible ? SWP_NOACTIVATE : 0)); 
        return (LRESULT) 1; 
    } 
 
    // New palette stuff 
    if (uMsg == m_RealizePalette) { 
        ASSERT(m_hwnd == hwnd); 
        return OnPaletteChange(m_hwnd,WM_QUERYNEWPALETTE); 
    } 
 
    switch (uMsg) { 
 
        // Repaint the window if the system colours change 
 
        case WM_SYSCOLORCHANGE: 
 
            InvalidateRect(hwnd,NULL,FALSE); 
            return (LRESULT) 1; 
 
        // Somebody has changed the palette 
        case WM_PALETTECHANGED: 
 
            OnPaletteChange((HWND)wParam,uMsg); 
            return (LRESULT) 0; 
 
        // We are about to receive the keyboard focus so we ask GDI to realise 
        // our logical palette again and hopefully it will be fully installed 
        // without any mapping having to be done during any picture rendering 
 
case WM_QUERYNEWPALETTE: 
    ASSERT(m_hwnd == hwnd); 
            return OnPaletteChange(m_hwnd,uMsg); 
 
        // Store the width and height as useful base class members 
 
        case WM_SIZE: 
 
    OnSize(LOWORD(lParam), HIWORD(lParam)); 
            return (LRESULT) 0; 
 
        // Intercept the WM_CLOSE messages to hide the window 
 
        case WM_CLOSE: 
 
            OnClose(); 
            return (LRESULT) 0; 
    } 
    return DefWindowProc(hwnd,uMsg,wParam,lParam); 
} 
 
 
// This handles the Windows palette change messages - if we do realise our 
// palette then we return TRUE otherwise we return FALSE. If our window is 
// foreground application then we should get first choice of colours in the 
// system palette entries. We get best performance when our logical palette 
// includes the standard VGA colours (at the beginning and end) otherwise 
// GDI may have to map from our palette to the device palette while drawing 
 
LRESULT CBaseWindow::OnPaletteChange(HWND hwnd,UINT Message) 
{ 
    // First check we are not changing the palette during closedown 
 
    if (m_hwnd == NULL || hwnd == NULL) { 
        return (LRESULT) 0; 
    } 
    ASSERT(!m_bRealizing); 
 
    // Should we realise our palette again 
 
    if ((Message == WM_QUERYNEWPALETTE || hwnd != m_hwnd)) { 
        //  It seems that even if we're invisible that we can get asked 
        //  to realize our palette and this can cause really ugly side-effects 
        //  Seems like there's another bug but this masks it a least for the 
        //  shutting down case. 
        if (!IsWindowVisible(m_hwnd)) { 
            DbgLog((LOG_TRACE, 1, TEXT("Realizing when invisible!"))); 
            return (LRESULT) 0; 
        } 
 
        // Avoid recursion with multiple graphs in the same app 
#ifdef DEBUG 
        m_bRealizing = TRUE; 
#endif 
        DoRealisePalette(Message != WM_QUERYNEWPALETTE); 
#ifdef DEBUG 
        m_bRealizing = FALSE; 
#endif 
 
        // Should we redraw the window with the new palette 
        if (Message == WM_PALETTECHANGED) { 
            InvalidateRect(m_hwnd,NULL,FALSE); 
        } 
    } 
 
    return (LRESULT) 1; 
} 
 
 
// Return the default window rectangle 
 
RECT CBaseWindow::GetDefaultRect() 
{ 
    RECT DefaultRect = {0,0,DEFWIDTH,DEFHEIGHT}; 
    ASSERT(m_hwnd); 
    // ASSERT(m_hdc); 
    return DefaultRect; 
} 
 
 
// Return the current window width 
 
LONG CBaseWindow::GetWindowWidth() 
{ 
    ASSERT(m_hwnd); 
    // ASSERT(m_hdc); 
    return m_Width; 
} 
 
 
// Return the current window height 
 
LONG CBaseWindow::GetWindowHeight() 
{ 
    ASSERT(m_hwnd); 
    // ASSERT(m_hdc); 
    return m_Height; 
} 
 
 
// Return the window handle 
 
HWND CBaseWindow::GetWindowHWND() 
{ 
    ASSERT(m_hwnd); 
    // ASSERT(m_hdc); 
    return m_hwnd; 
} 
 
 
// Return the window drawing device context 
 
HDC CBaseWindow::GetWindowHDC() 
{ 
    ASSERT(m_hwnd); 
    ASSERT(m_hdc); 
    return m_hdc; 
} 
 
 
// Return the offscreen window drawing device context 
 
HDC CBaseWindow::GetMemoryHDC() 
{ 
    ASSERT(m_hwnd); 
    ASSERT(m_MemoryDC); 
    return m_MemoryDC; 
} 
 
 
// This is available to clients who want to change the window visiblity. It's 
// little more than an indirection to the Win32 ShowWindow although these is 
// some benefit in going through here as this function may change sometime 
 
HRESULT CBaseWindow::DoShowWindow(LONG ShowCmd) 
{ 
    ShowWindow(m_hwnd,ShowCmd); 
    return NOERROR; 
} 
 
 
// Generate a WM_PAINT message for the video window 
 
void CBaseWindow::PaintWindow(BOOL bErase) 
{ 
    InvalidateRect(m_hwnd,NULL,bErase); 
} 
 
 
// Allow an application to have us set the video window in the foreground. We 
// have this because it is difficult for one thread to do do this to a window 
// owned by another thread. Rather than expose the message we use to execute 
// the inter thread send message we provide the interface function. All we do 
// is to SendMessage to the video window renderer thread with a WM_SHOWSTAGE 
 
void CBaseWindow::DoSetWindowForeground(BOOL bFocus) 
{ 
    SendMessage(m_hwnd,m_ShowStageMessage,(WPARAM) bFocus,(LPARAM) 0); 
} 
 
 
// Constructor initialises the owning object pointer. Since we are a worker 
// class for the main window object we have relatively few state variables to 
// look after. We are given device context handles to use later on as well as 
// the source and destination rectangles (but reset them here just in case) 
 
CDrawImage::CDrawImage(CBaseWindow *pBaseWindow) : 
    m_pBaseWindow(pBaseWindow), 
    m_hdc(NULL), 
    m_MemoryDC(NULL), 
    m_bStretch(FALSE), 
    m_pMediaType(NULL), 
    m_bUsingImageAllocator(FALSE) 
{ 
    ASSERT(pBaseWindow); 
    ResetPaletteVersion(); 
    SetRectEmpty(&m_TargetRect); 
    SetRectEmpty(&m_SourceRect); 
 
    m_perfidRenderTime = MSR_REGISTER("Single Blt time"); 
} 
 
 
// Overlay the image time stamps on the picture. Access to this method is 
// serialised by the caller. We display the sample start and end times on 
// top of the video using TextOut on the device context we are handed. If 
// there isn't enough room in the window for the times we don't show them 
 
void CDrawImage::DisplaySampleTimes(IMediaSample *pSample) 
{ 
    TCHAR szTimes[TIMELENGTH];      // Time stamp strings 
    ASSERT(pSample);                // Quick sanity check 
    RECT ClientRect;                // Client window size 
    SIZE Size;                      // Size of text output 
 
    // Get the time stamps and window size 
 
    pSample->GetTime((REFERENCE_TIME*)&m_StartSample, (REFERENCE_TIME*)&m_EndSample); 
    HWND hwnd = m_pBaseWindow->GetWindowHWND(); 
    EXECUTE_ASSERT(GetClientRect(hwnd,&ClientRect)); 
 
    // Format the sample time stamps 
 
    wsprintf(szTimes,TEXT("%08d : %08d"), 
             m_StartSample.Millisecs(), 
             m_EndSample.Millisecs()); 
 
    ASSERT(lstrlen(szTimes) < TIMELENGTH); 
 
    // Put the times in the middle at the bottom of the window 
 
    GetTextExtentPoint32(m_hdc,szTimes,lstrlen(szTimes),&Size); 
    INT XPos = ((ClientRect.right - ClientRect.left) - Size.cx) / 2; 
    INT YPos = ((ClientRect.bottom - ClientRect.top) - Size.cy) * 4 / 5; 
 
    // Check the window is big enough to have sample times displayed 
 
    if ((XPos > 0) && (YPos > 0)) { 
        TextOut(m_hdc,XPos,YPos,szTimes,lstrlen(szTimes)); 
    } 
} 
 
 
// This is called when the drawing code sees that the image has a down level 
// palette cookie. We simply call the SetDIBColorTable Windows API with the 
// palette that is found after the BITMAPINFOHEADER - we return no errors 
 
void CDrawImage::UpdateColourTable(HDC hdc,BITMAPINFOHEADER *pbmi) 
{ 
    ASSERT(pbmi->biClrUsed); 
    RGBQUAD *pColourTable = (RGBQUAD *)(pbmi+1); 
 
    // Set the new palette in the device context 
 
    UINT uiReturn = SetDIBColorTable(hdc,(UINT) 0, 
                                     pbmi->biClrUsed, 
                                     pColourTable); 
 
    // Should always succeed but check in debug builds 
    ASSERT(uiReturn == pbmi->biClrUsed); 
} 
 
 
// No source rectangle scaling is done by the base class 
 
RECT CDrawImage::ScaleSourceRect(const RECT *pSource) 
{ 
    ASSERT(pSource); 
    return *pSource; 
} 
 
 
// This is called when the funky output pin uses our allocator. The samples we 
// allocate are special because the memory is shared between us and GDI thus 
// removing one copy when we ask for the image to be rendered. The source type 
// information is in the main renderer m_mtIn field which is initialised when 
// the media type is agreed in SetMediaType, the media type may be changed on 
// the fly if, for example, the source filter needs to change the palette 
 
void CDrawImage::FastRender(IMediaSample *pMediaSample) 
{ 
    BITMAPINFOHEADER *pbmi;     // Image format data 
    DIBDATA *pDibData;          // Stores DIB information 
    BYTE *pImage;               // Pointer to image data 
    HBITMAP hOldBitmap;         // Store the old bitmap 
    CImageSample *pSample;      // Pointer to C++ object 
 
    ASSERT(m_pMediaType); 
 
    // From the untyped source format block get the VIDEOINFO and subsequently 
// the BITMAPINFOHEADER structure. We can cast the IMediaSample interface 
    // to a CImageSample object so we can retrieve it's DIBSECTION details 
 
    pbmi = HEADER(m_pMediaType->Format()); 
    pSample = (CImageSample *) pMediaSample; 
    pDibData = pSample->GetDIBData(); 
    hOldBitmap = (HBITMAP) SelectObject(m_MemoryDC,pDibData->hBitmap); 
 
    // Get a pointer to the real image data 
 
    HRESULT hr = pMediaSample->GetPointer(&pImage); 
    if (FAILED(hr)) { 
        return; 
    } 
 
    // Do we need to update the colour table, we increment our palette cookie 
    // each time we get a dynamic format change. The sample palette cookie is 
    // stored in the DIBDATA structure so we try to keep the fields in sync 
    // By the time we get to draw the images the format change will be done 
    // so all we do is ask the renderer for what it's palette version is 
 
    if (pDibData->PaletteVersion < GetPaletteVersion()) { 
        ASSERT(pbmi->biBitCount <= iPALETTE); 
        UpdateColourTable(m_MemoryDC,pbmi); 
        pDibData->PaletteVersion = GetPaletteVersion(); 
    } 
 
    // This allows derived classes to change the source rectangle that we do 
    // the drawing with. For example a renderer may ask a codec to stretch 
    // the video from 320x240 to 640x480, in which case the source we see in 
    // here will still be 320x240, although the source we want to draw with 
    // should be scaled up to 640x480. The base class implementation of this 
    // method does nothing but return the same rectangle as we are passed in 
 
    RECT SourceRect = ScaleSourceRect(&m_SourceRect); 
 
    // Is the window the same size as the video 
 
    if (m_bStretch == FALSE) { 
 
        // Put the image straight into the window 
 
        BitBlt( 
            (HDC) m_hdc,                            // Target device HDC 
            m_TargetRect.left,                      // X sink position 
            m_TargetRect.top,                       // Y sink position 
            m_TargetRect.right - m_TargetRect.left, // Destination width 
            m_TargetRect.bottom - m_TargetRect.top, // Destination height 
            m_MemoryDC,                             // Source device context 
            SourceRect.left,                        // X source position 
            SourceRect.top,                         // Y source position 
            SRCCOPY);                               // Simple copy 
 
    } else { 
 
        // Stretch the image when copying to the window 
 
        StretchBlt( 
            (HDC) m_hdc,                            // Target device HDC 
            m_TargetRect.left,                      // X sink position 
            m_TargetRect.top,                       // Y sink position 
            m_TargetRect.right - m_TargetRect.left, // Destination width 
            m_TargetRect.bottom - m_TargetRect.top, // Destination height 
            m_MemoryDC,                             // Source device HDC 
            SourceRect.left,                        // X source position 
            SourceRect.top,                         // Y source position 
            SourceRect.right - SourceRect.left,     // Source width 
            SourceRect.bottom - SourceRect.top,     // Source height 
            SRCCOPY);                               // Simple copy 
    } 
 
    // This displays the sample times over the top of the image. This used to 
    // draw the times into the offscreen device context however that actually 
    // writes the text into the image data buffer which may not be writable 
 
    #ifdef DEBUG 
    DisplaySampleTimes(pMediaSample); 
    #endif 
 
    // Put the old bitmap back into the device context so we don't leak 
    SelectObject(m_MemoryDC,hOldBitmap); 
} 
 
 
// This is called when there is a sample ready to be drawn, unfortunately the 
// output pin was being rotten and didn't choose our super excellent shared 
// memory DIB allocator so we have to do this slow render using boring old GDI 
// SetDIBitsToDevice and StretchDIBits. The down side of using these GDI 
// functions is that the image data has to be copied across from our address 
// space into theirs before going to the screen (although in reality the cost 
// is small because all they do is to map the buffer into their address space) 
 
void CDrawImage::SlowRender(IMediaSample *pMediaSample) 
{ 
    // Get the BITMAPINFOHEADER for the connection 
 
    ASSERT(m_pMediaType); 
    BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format()); 
    BYTE *pImage; 
 
    // Get the image data buffer 
 
    HRESULT hr = pMediaSample->GetPointer(&pImage); 
    if (FAILED(hr)) { 
        return; 
    } 
 
    // This allows derived classes to change the source rectangle that we do 
    // the drawing with. For example a renderer may ask a codec to stretch 
    // the video from 320x240 to 640x480, in which case the source we see in 
    // here will still be 320x240, although the source we want to draw with 
    // should be scaled up to 640x480. The base class implementation of this 
    // method does nothing but return the same rectangle as we are passed in 
 
    RECT SourceRect = ScaleSourceRect(&m_SourceRect); 
 
    LONG lAdjustedSourceTop = SourceRect.top; 
    // if the origin of bitmap is bottom-left, adjust soruce_rect_top 
    // to be the bottom-left corner instead of the top-left. 
    if (pbmi->biHeight > 0) { 
       lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom; 
    } 
    // Is the window the same size as the video 
 
    if (m_bStretch == FALSE) { 
 
        // Put the image straight into the window 
 
        SetDIBitsToDevice( 
            (HDC) m_hdc,                            // Target device HDC 
            m_TargetRect.left,                      // X sink position 
            m_TargetRect.top,                       // Y sink position 
            m_TargetRect.right - m_TargetRect.left, // Destination width 
            m_TargetRect.bottom - m_TargetRect.top, // Destination height 
            SourceRect.left,                        // X source position 
            lAdjustedSourceTop,                     // Adjusted Y source position 
            (UINT) 0,                               // Start scan line 
            pbmi->biHeight,                         // Scan lines present 
            pImage,                                 // Image data 
            (BITMAPINFO *) pbmi,                    // DIB header 
            DIB_RGB_COLORS);                        // Type of palette 
 
    } else { 
 
        // Stretch the image when copying to the window 
 
        StretchDIBits( 
            (HDC) m_hdc,                            // Target device HDC 
            m_TargetRect.left,                      // X sink position 
            m_TargetRect.top,                       // Y sink position 
            m_TargetRect.right - m_TargetRect.left, // Destination width 
            m_TargetRect.bottom - m_TargetRect.top, // Destination height 
            SourceRect.left,                        // X source position 
            lAdjustedSourceTop,                     // Adjusted Y source position 
            SourceRect.right - SourceRect.left,     // Source width 
            SourceRect.bottom - SourceRect.top,     // Source height 
            pImage,                                 // Image data 
            (BITMAPINFO *) pbmi,                    // DIB header 
            DIB_RGB_COLORS,                         // Type of palette 
            SRCCOPY);                               // Simple image copy 
    } 
 
    // This shows the sample reference times over the top of the image which 
    // looks a little flickery. I tried using GdiSetBatchLimit and GdiFlush to 
    // control the screen updates but it doesn't quite work as expected and 
    // only partially reduces the flicker. I also tried using a memory context 
    // and combining the two in that before doing a final BitBlt operation to 
    // the screen, unfortunately this has considerable performance penalties 
    // and also means that this code is not executed when compiled retail 
 
    #ifdef DEBUG 
    DisplaySampleTimes(pMediaSample); 
    #endif 
} 
 
 
// This is called with an IMediaSample interface on the image to be drawn. We 
// decide on the drawing mechanism based on who's allocator we are using. We 
// may be called when the window wants an image painted by WM_PAINT messages 
// We can't realise the palette here because we have the renderer lock, any 
// call to realise may cause an interthread send message to the window thread 
// which may in turn be waiting to get the renderer lock before servicing it 
 
BOOL CDrawImage::DrawImage(IMediaSample *pMediaSample) 
{ 
    ASSERT(m_hdc); 
    ASSERT(m_MemoryDC); 
    NotifyStartDraw(); 
 
    // If the output pin used our allocator then the samples passed are in 
    // fact CVideoSample objects that contain CreateDIBSection data that we 
    // use to do faster image rendering, they may optionally also contain a 
    // DirectDraw surface pointer in which case we do not do the drawing 
 
    if (m_bUsingImageAllocator == FALSE) { 
        SlowRender(pMediaSample); 
        EXECUTE_ASSERT(GdiFlush()); 
        NotifyEndDraw(); 
        return TRUE; 
    } 
 
    // This is a DIBSECTION buffer 
 
    FastRender(pMediaSample); 
    EXECUTE_ASSERT(GdiFlush()); 
    NotifyEndDraw(); 
    return TRUE; 
} 
 
 
// This is called by the owning window object after it has created the window 
// and it's drawing contexts. We are constructed with the base window we'll 
// be drawing into so when given the notification we retrive the device HDCs 
// to draw with. We cannot call these in our constructor as they are virtual 
 
void CDrawImage::SetDrawContext() 
{ 
    m_MemoryDC = m_pBaseWindow->GetMemoryHDC(); 
    m_hdc = m_pBaseWindow->GetWindowHDC(); 
} 
 
 
// This is called to set the target rectangle in the video window, it will be 
// called whenever a WM_SIZE message is retrieved from the message queue. We 
// simply store the rectangle and use it later when we do the drawing calls 
 
void CDrawImage::SetTargetRect(RECT *pTargetRect) 
{ 
    ASSERT(pTargetRect); 
    m_TargetRect = *pTargetRect; 
    SetStretchMode(); 
} 
 
 
// Return the current target rectangle 
 
void CDrawImage::GetTargetRect(RECT *pTargetRect) 
{ 
    ASSERT(pTargetRect); 
    *pTargetRect = m_TargetRect; 
} 
 
 
// This is called when we want to change the section of the image to draw. We 
// use this information in the drawing operation calls later on. We must also 
// see if the source and destination rectangles have the same dimensions. If 
// not we must stretch during the drawing rather than a direct pixel copy 
 
void CDrawImage::SetSourceRect(RECT *pSourceRect) 
{ 
    ASSERT(pSourceRect); 
    m_SourceRect = *pSourceRect; 
    SetStretchMode(); 
} 
 
 
// Return the current source rectangle 
 
void CDrawImage::GetSourceRect(RECT *pSourceRect) 
{ 
    ASSERT(pSourceRect); 
    *pSourceRect = m_SourceRect; 
} 
 
 
// This is called when either the source or destination rectanges change so we 
// can update the stretch flag. If the rectangles don't match we stretch the 
// video during the drawing otherwise we call the fast pixel copy functions 
// NOTE the source and/or the destination rectangle may be completely empty 
 
void CDrawImage::SetStretchMode() 
{ 
    // Calculate the overall rectangle dimensions 
 
    LONG SourceWidth = m_SourceRect.right - m_SourceRect.left; 
    LONG SinkWidth = m_TargetRect.right - m_TargetRect.left; 
    LONG SourceHeight = m_SourceRect.bottom - m_SourceRect.top; 
    LONG SinkHeight = m_TargetRect.bottom - m_TargetRect.top; 
 
    m_bStretch = TRUE; 
    if (SourceWidth == SinkWidth) { 
        if (SourceHeight == SinkHeight) { 
            m_bStretch = FALSE; 
        } 
    } 
} 
 
 
// Tell us whose allocator we are using. This should be called with TRUE if 
// the filter agrees to use an allocator based around the CImageAllocator 
// SDK base class - whose image buffers are made through CreateDIBSection. 
// Otherwise this should be called with FALSE and we will draw the images 
// using SetDIBitsToDevice and StretchDIBitsToDevice. None of these calls 
// can handle buffers which have non zero strides (like DirectDraw uses) 
 
void CDrawImage::NotifyAllocator(BOOL bUsingImageAllocator) 
{ 
    m_bUsingImageAllocator = bUsingImageAllocator; 
} 
 
 
// Are we using the image DIBSECTION allocator 
 
BOOL CDrawImage::UsingImageAllocator() 
{ 
    return m_bUsingImageAllocator; 
} 
 
 
// We need the media type of the connection so that we can get the BITMAPINFO 
// from it. We use that in the calls to draw the image such as StretchDIBits 
// and also when updating the colour table held in shared memory DIBSECTIONs 
 
void CDrawImage::NotifyMediaType(CMediaType *pMediaType) 
{ 
    m_pMediaType = pMediaType; 
} 
 
 
// We store in this object a cookie maintaining the current palette version. 
// Each time a palettised format is changed we increment this value so that 
// when we come to draw the images we look at the colour table value they 
// have and if less than the current we know to update it. This version is 
// only needed and indeed used when working with shared memory DIBSECTIONs 
 
LONG CDrawImage::GetPaletteVersion() 
{ 
    return m_PaletteVersion; 
} 
 
 
// Resets the current palette version number 
 
void CDrawImage::ResetPaletteVersion() 
{ 
    m_PaletteVersion = PALETTE_VERSION; 
} 
 
 
// Increment the current palette version 
 
void CDrawImage::IncrementPaletteVersion() 
{ 
    m_PaletteVersion++; 
} 
 
 
// Constructor must initialise the base allocator. Each sample we create has a 
// palette version cookie on board. When the source filter changes the palette 
// during streaming the window object increments an internal cookie counter it 
// keeps as well. When it comes to render the samples it looks at the cookie 
// values and if they don't match then it knows to update the sample's colour 
// table. However we always create samples with a cookie of PALETTE_VERSION 
// If there have been multiple format changes and we disconnect and reconnect 
// thereby causing the samples to be reallocated we will create them with a 
// cookie much lower than the current version, this isn't a problem since it 
// will be seen by the window object and the versions will then be updated 
 
CImageAllocator::CImageAllocator(CBaseFilter *pFilter, 
                                 TCHAR *pName, 
                                 HRESULT *phr) : 
    CBaseAllocator(pName,NULL,phr), 
    m_pFilter(pFilter) 
{ 
    ASSERT(phr); 
    ASSERT(pFilter); 
} 
 
 
// Check our DIB buffers have been released 
 
#ifdef DEBUG 
CImageAllocator::~CImageAllocator() 
{ 
    ASSERT(m_bCommitted == FALSE); 
} 
#endif 
 
 
// Called from destructor and also from base class to free resources. We work 
// our way through the list of media samples deleting the DIBSECTION created 
// for each. All samples should be back in our list so there is no chance a 
// filter is still using one to write on the display or hold on a pending list 
 
void CImageAllocator::Free() 
{ 
    ASSERT(m_lAllocated == m_lFree.GetCount()); 
    EXECUTE_ASSERT(GdiFlush()); 
    CImageSample *pSample; 
    DIBDATA *pDibData; 
 
    while (m_lFree.GetCount() != 0) { 
        pSample = (CImageSample *) m_lFree.RemoveHead(); 
        pDibData = pSample->GetDIBData(); 
        EXECUTE_ASSERT(DeleteObject(pDibData->hBitmap)); 
        EXECUTE_ASSERT(CloseHandle(pDibData->hMapping)); 
        delete pSample; 
    } 
 
    m_lAllocated = 0; 
} 
 
 
// Prepare the allocator by checking all the input parameters 
 
STDMETHODIMP CImageAllocator::CheckSizes(ALLOCATOR_PROPERTIES *pRequest) 
{ 
    // Check we have a valid connection 
 
    if (m_pMediaType == NULL) { 
        return VFW_E_NOT_CONNECTED; 
    } 
 
    // NOTE We always create a DIB section with the source format type which 
    // may contain a source palette. When we do the BitBlt drawing operation 
    // the target display device may contain a different palette (we may not 
    // have the focus) in which case GDI will do after the palette mapping 
 
    VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *) m_pMediaType->Format(); 
 
    // When we call CreateDIBSection it implicitly maps only enough memory 
    // for the image as defined by thee BITMAPINFOHEADER. If the user asks 
    // for an image smaller than this then we reject the call, if they ask 
    // for an image larger than this then we return what they can have 
 
    if ((DWORD) pRequest->cbBuffer < pVideoInfo->bmiHeader.biSizeImage) { 
        return E_INVALIDARG; 
    } 
 
    // Reject buffer prefixes 
 
    if (pRequest->cbPrefix > 0) { 
        return E_INVALIDARG; 
    } 
 
    pRequest->cbBuffer = pVideoInfo->bmiHeader.biSizeImage; 
    return NOERROR; 
} 
 
 
// Agree the number of media sample buffers and their sizes. The base class 
// this allocator is derived from allows samples to be aligned only on byte 
// boundaries NOTE the buffers are not allocated until the Commit call 
 
STDMETHODIMP CImageAllocator::SetProperties( 
    ALLOCATOR_PROPERTIES * pRequest, 
    ALLOCATOR_PROPERTIES * pActual) 
{ 
    ALLOCATOR_PROPERTIES Adjusted = *pRequest; 
 
    // Check the parameters fit with the current connection 
 
    HRESULT hr = CheckSizes(&Adjusted); 
    if (FAILED(hr)) { 
        return hr; 
    } 
    return CBaseAllocator::SetProperties(&Adjusted, pActual); 
} 
 
 
// Commit the memory by allocating the agreed number of media samples. For 
// each sample we are committed to creating we have a CImageSample object 
// that we use to manage it's resources. This is initialised with a DIBDATA 
// structure that contains amongst other things the GDI DIBSECTION handle 
// We will access the renderer media type during this so we must have locked 
// (to prevent the format changing for example). The class overrides Commit 
// and Decommit to do this locking (base class Commit in turn calls Alloc) 
 
HRESULT CImageAllocator::Alloc(void) 
{ 
    ASSERT(m_pMediaType); 
    CImageSample *pSample; 
    DIBDATA DibData; 
 
    // Check the base allocator says it's ok to continue 
 
    HRESULT hr = CBaseAllocator::Alloc(); 
    if (FAILED(hr)) { 
        return hr; 
    } 
 
    // We create a new memory mapped object although we don't map it into our 
    // address space because GDI does that in CreateDIBSection. It is possible 
    // that we run out of resources before creating all the samples in which 
    // case the available sample list is left with those already created 
 
    ASSERT(m_lAllocated == 0); 
    while (m_lAllocated < m_lCount) { 
 
        // Create and initialise a shared memory GDI buffer 
 
        HRESULT hr = CreateDIB(m_lSize,DibData); 
        if (FAILED(hr)) { 
            return hr; 
        } 
 
        // Create the sample object and pass it the DIBDATA 
 
        pSample = CreateImageSample(DibData.pBase,m_lSize); 
        if (pSample == NULL) { 
            EXECUTE_ASSERT(DeleteObject(DibData.hBitmap)); 
            EXECUTE_ASSERT(CloseHandle(DibData.hMapping)); 
            return E_OUTOFMEMORY; 
        } 
 
        // Add the completed sample to the available list 
 
        pSample->SetDIBData(&DibData); 
        m_lFree.Add(pSample); 
        m_lAllocated++; 
    } 
    return NOERROR; 
} 
 
 
// We have a virtual method that allocates the samples so that a derived class 
// may override it and allocate more specialised sample objects. So long as it 
// derives its samples from CImageSample then all this code will still work ok 
 
CImageSample *CImageAllocator::CreateImageSample(LPBYTE pData,LONG Length) 
{ 
    HRESULT hr = NOERROR; 
    CImageSample *pSample; 
 
    // Allocate the new sample and check the return codes 
 
    pSample = new CImageSample((CBaseAllocator *) this,   // Base class 
                               NAME("Video sample"),      // DEBUG name 
                               (HRESULT *) &hr,           // Return code 
                               (LPBYTE) pData,            // DIB address 
                               (LONG) Length);            // Size of DIB 
 
    if (pSample == NULL || FAILED(hr)) { 
        delete pSample; 
        return NULL; 
    } 
    return pSample; 
} 
 
 
// This function allocates a shared memory block for use by the source filter 
// generating DIBs for us to render. The memory block is created in shared 
// memory so that GDI doesn't have to copy the memory when we do a BitBlt 
 
HRESULT CImageAllocator::CreateDIB(LONG InSize,DIBDATA &DibData) 
{ 
    BITMAPINFO *pbmi;       // Format information for pin 
    BYTE *pBase;            // Pointer to the actual image 
    HANDLE hMapping;        // Handle to mapped object 
    HBITMAP hBitmap;        // DIB section bitmap handle 
 
    // Create a file mapping object and map into our address space 
 
    hMapping = CreateFileMapping(hMEMORY,         // Use system page file 
                                 NULL,            // No security attributes 
                                 PAGE_READWRITE,  // Full access to memory 
                                 (DWORD) 0,       // Less than 4Gb in size 
                                 InSize,          // Size of buffer 
                                 NULL);           // No name to section 
    if (hMapping == NULL) { 
        DWORD Error = GetLastError(); 
        return HRESULT_FROM_WIN32(Error); 
    } 
 
    // NOTE We always create a DIB section with the source format type which 
    // may contain a source palette. When we do the BitBlt drawing operation 
    // the target display device may contain a different palette (we may not 
    // have the focus) in which case GDI will do after the palette mapping 
 
    pbmi = (BITMAPINFO *) HEADER(m_pMediaType->Format()); 
    if (m_pMediaType == NULL) { 
        DbgBreak("Invalid media type"); 
    } 
 
    hBitmap = CreateDIBSection((HDC) NULL,          // NO device context 
                               pbmi,                // Format information 
                               DIB_RGB_COLORS,      // Use the palette 
                               (VOID **) &pBase,    // Pointer to image data 
                               hMapping,            // Mapped memory handle 
                               (DWORD) 0);          // Offset into memory 
 
    if (hBitmap == NULL || pBase == NULL) { 
        EXECUTE_ASSERT(CloseHandle(hMapping)); 
        DWORD Error = GetLastError(); 
        return HRESULT_FROM_WIN32(Error); 
    } 
 
    // Initialise the DIB information structure 
 
    DibData.hBitmap = hBitmap; 
    DibData.hMapping = hMapping; 
    DibData.pBase = pBase; 
    DibData.PaletteVersion = PALETTE_VERSION; 
    GetObject(hBitmap,sizeof(DIBSECTION),(VOID *)&DibData.DibSection); 
 
    return NOERROR; 
} 
 
 
// We use the media type during the DIBSECTION creation 
 
void CImageAllocator::NotifyMediaType(CMediaType *pMediaType) 
{ 
    m_pMediaType = pMediaType; 
} 
 
 
// Overriden to increment the owning object's reference count 
 
STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingAddRef() 
{ 
    return m_pFilter->AddRef(); 
} 
 
 
// Overriden to decrement the owning object's reference count 
 
STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingRelease() 
{ 
    return m_pFilter->Release(); 
} 
 
 
// If you derive a class from CMediaSample that has to transport specialised 
// member variables and entry points then there are three alternate solutions 
// The first is to create a memory buffer larger than actually required by the 
// sample and store your information either at the beginning of it or at the 
// end, the former being moderately safer allowing for misbehaving transform 
// filters. You then adjust the buffer address when you create the base media 
// sample. This has the disadvantage of breaking up the memory allocated to 
// the samples into separate blocks. The second solution is to implement a 
// class derived from CMediaSample and support additional interface(s) that 
// convey your private data. This means defining a custom interface. The final 
// alternative is to create a class that inherits from CMediaSample and adds 
// the private data structures, when you get an IMediaSample in your Receive() 
// call check to see if your allocator is being used, and if it is then cast 
// the IMediaSample into one of your objects. Additional checks can be made 
// to ensure the sample's this pointer is known to be one of your own objects 
 
CImageSample::CImageSample(CBaseAllocator *pAllocator, 
                           TCHAR *pName, 
                           HRESULT *phr, 
                           LPBYTE pBuffer, 
                           LONG length) : 
    CMediaSample(pName,pAllocator,phr,pBuffer,length), 
    m_bInit(FALSE) 
{ 
    ASSERT(pAllocator); 
    ASSERT(pBuffer); 
} 
 
 
// Set the shared memory DIB information 
 
void CImageSample::SetDIBData(DIBDATA *pDibData) 
{ 
    ASSERT(pDibData); 
    m_DibData = *pDibData; 
    m_bInit = TRUE; 
} 
 
 
// Retrieve the shared memory DIB data 
 
DIBDATA *CImageSample::GetDIBData() 
{ 
    ASSERT(m_bInit == TRUE); 
    return &m_DibData; 
} 
 
 
// This class handles the creation of a palette. It is fairly specialist and 
// is intended to simplify palette management for video renderer filters. It 
// is for this reason that the constructor requires three other objects with 
// which it interacts, namely a base media filter, a base window and a base 
// drawing object although the base window or the draw object may be NULL to 
// ignore that part of us. We try not to create and install palettes unless 
// absolutely necessary as they typically require WM_PALETTECHANGED messages 
// to be sent to every window thread in the system which is very expensive 
 
CImagePalette::CImagePalette(CBaseFilter *pBaseFilter, 
                             CBaseWindow *pBaseWindow, 
                             CDrawImage *pDrawImage) : 
    m_pBaseWindow(pBaseWindow), 
    m_pFilter(pBaseFilter), 
    m_pDrawImage(pDrawImage), 
    m_hPalette(NULL) 
{ 
    ASSERT(m_pFilter); 
} 
 
 
// Destructor 
 
#ifdef DEBUG 
CImagePalette::~CImagePalette() 
{ 
    ASSERT(m_hPalette == NULL); 
} 
#endif 
 
 
// We allow dynamic format changes of the palette but rather than change the 
// palette every time we call this to work out whether an update is required. 
// If the original type didn't use a palette and the new one does (or vica 
// versa) then we return TRUE. If neither formats use a palette we'll return 
// FALSE. If both formats use a palette we compare their colours and return 
// FALSE if they match. This therefore short circuits palette creation unless 
// absolutely necessary since installing palettes is an expensive operation 
 
BOOL CImagePalette::ShouldUpdate(const VIDEOINFOHEADER *pNewInfo, 
                                 const VIDEOINFOHEADER *pOldInfo) 
{ 
    // We may not have a current format yet 
 
    if (pOldInfo == NULL) { 
        return TRUE; 
    } 
 
    // Do both formats not require a palette 
 
    if (ContainsPalette(pNewInfo) == FALSE) { 
        if (ContainsPalette(pOldInfo) == FALSE) { 
            return FALSE; 
        } 
    } 
 
    // Compare the colours to see if they match 
 
    DWORD VideoEntries = pNewInfo->bmiHeader.biClrUsed; 
    if (ContainsPalette(pNewInfo) == TRUE) 
        if (ContainsPalette(pOldInfo) == TRUE) 
            if (pOldInfo->bmiHeader.biClrUsed == VideoEntries) 
                if (pOldInfo->bmiHeader.biClrUsed > 0) 
                    if (memcmp((PVOID) GetBitmapPalette(pNewInfo), 
                               (PVOID) GetBitmapPalette(pOldInfo), 
                               VideoEntries * sizeof(RGBQUAD)) == 0) { 
 
                        return FALSE; 
                    } 
    return TRUE; 
} 
 
 
// This is normally called when the input pin type is set to install a palette 
// We will typically be called from two different places. The first is when we 
// have negotiated a palettised media type after connection, the other is when 
// we receive a new type during processing with an updated palette in which 
// case we must remove and release the resources held by the current palette 
 
// We can be passed an optional device name if we wish to prepare a palette 
// for a specific monitor on a multi monitor system 
 
HRESULT CImagePalette::PreparePalette(const CMediaType *pmtNew, 
                                      const CMediaType *pmtOld, 
      LPSTR szDevice) 
{ 
    const VIDEOINFOHEADER *pNewInfo = (VIDEOINFOHEADER *) pmtNew->Format(); 
    const VIDEOINFOHEADER *pOldInfo = (VIDEOINFOHEADER *) pmtOld->Format(); 
    ASSERT(pNewInfo); 
 
    // This is an performance optimisation, when we get a media type we check 
    // to see if the format requires a palette change. If either we need one 
    // when previously we didn't or vica versa then this returns TRUE, if we 
    // previously needed a palette and we do now it compares their colours 
 
    if (ShouldUpdate(pNewInfo,pOldInfo) == FALSE) { 
        NOTE("No update needed"); 
        return S_FALSE; 
    } 
 
    // We must notify the filter graph that the application may have changed 
    // the palette although in practice we don't bother checking to see if it 
    // is really different. If it tries to get the palette either the window 
    // or renderer lock will ensure it doesn't get in until we are finished 
    RemovePalette(); 
    m_pFilter->NotifyEvent(EC_PALETTE_CHANGED,0,0); 
 
    // Do we need a palette for the new format 
 
    if (ContainsPalette(pNewInfo) == FALSE) { 
        NOTE("New has no palette"); 
        return S_FALSE; 
    } 
 
    // If we're changing the palette on the fly then we increment our palette 
    // cookie which is compared against the cookie also stored in all of our 
    // DIBSECTION media samples. If they don't match when we come to draw it 
    // then we know the sample is out of date and we'll update it's palette 
 
    NOTE("Making new colour palette"); 
    m_hPalette = MakePalette(pNewInfo, szDevice); 
    ASSERT(m_hPalette != NULL); 
 
    // The window in which the new palette is to be realised may be a NULL 
    // pointer to signal that no window is in use, if so we don't call it 
    // Some filters just want to use this object to create/manage palettes 
 
    if (m_pBaseWindow) m_pBaseWindow->SetPalette(m_hPalette); 
 
    // This is the only time where we need access to the draw object to say 
    // to it that a new palette will be arriving on a sample real soon. The 
    // constructor may take a NULL pointer in which case we don't call this 
 
    if (m_pDrawImage) m_pDrawImage->IncrementPaletteVersion(); 
    return NOERROR; 
} 
 
 
// Helper function to copy a palette out of any kind of VIDEOINFO (ie it may 
// be YUV or true colour) into a palettised VIDEOINFO. We use this changing 
// palettes on DirectDraw samples as a source filter can attach a palette to 
// any buffer (eg YUV) and hand it back. We make a new palette out of that 
// format and then copy the palette colours into the current connection type 
 
HRESULT CImagePalette::CopyPalette(const CMediaType *pSrc,CMediaType *pDest) 
{ 
    // Reset the destination palette before starting 
 
    VIDEOINFOHEADER *pDestInfo = (VIDEOINFOHEADER *) pDest->Format(); 
    pDestInfo->bmiHeader.biClrUsed = 0; 
    pDestInfo->bmiHeader.biClrImportant = 0; 
 
    // Does the destination have a palette 
 
    if (PALETTISED(pDestInfo) == FALSE) { 
        NOTE("No destination palette"); 
        return S_FALSE; 
    } 
 
    // Does the source contain a palette 
 
    const VIDEOINFOHEADER *pSrcInfo = (VIDEOINFOHEADER *) pSrc->Format(); 
    if (ContainsPalette(pSrcInfo) == FALSE) { 
        NOTE("No source palette"); 
        return S_FALSE; 
    } 
 
    // The number of colours may be zero filled 
 
    DWORD PaletteEntries = pSrcInfo->bmiHeader.biClrUsed; 
    if (PaletteEntries == 0) { 
        DWORD Maximum  = (1 << pSrcInfo->bmiHeader.biBitCount); 
        NOTE1("Setting maximum colours (%d)",Maximum); 
        PaletteEntries = Maximum; 
    } 
 
    // Make sure the destination has enough room for the palette 
 
    ASSERT(pSrcInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS); 
    ASSERT(pSrcInfo->bmiHeader.biClrImportant <= PaletteEntries); 
    ASSERT(COLORS(pDestInfo) == GetBitmapPalette(pDestInfo)); 
    pDestInfo->bmiHeader.biClrUsed = PaletteEntries; 
    pDestInfo->bmiHeader.biClrImportant = pSrcInfo->bmiHeader.biClrImportant; 
    ULONG BitmapSize = GetBitmapFormatSize(HEADER(pSrcInfo)); 
 
    if (pDest->FormatLength() < BitmapSize) { 
        NOTE("Reallocating destination"); 
        pDest->ReallocFormatBuffer(BitmapSize); 
    } 
 
    // Now copy the palette colours across 
 
    CopyMemory((PVOID) COLORS(pDestInfo), 
               (PVOID) GetBitmapPalette(pSrcInfo), 
               PaletteEntries * sizeof(RGBQUAD)); 
 
    return NOERROR; 
} 
 
 
// This is normally called when the palette is changed (typically during a 
// dynamic format change) to remove any palette we previously installed. We 
// replace it (if necessary) in the video window with a standard VGA palette 
// that should always be available even if this is a true colour display 
 
HRESULT CImagePalette::RemovePalette() 
{ 
    // Do we have a palette to remove 
 
    if (m_hPalette == NULL) { 
        return NOERROR; 
    } 
 
    // Get a standard VGA colour palette 
 
    HPALETTE hPalette = (HPALETTE) GetStockObject(DEFAULT_PALETTE); 
    ASSERT(hPalette); 
    const HPALETTE hPalOurs = m_hPalette; 
 
    // Install the standard palette and delete ours. As in the previous method 
    // we may not have been given a window in the constructor to use and if we 
    // didn't then don't try to install the stock palette in it. This is used 
    // by filters that have to create palettes but who do not draw using GDI 
 
    if (m_pBaseWindow) { 
        SelectPalette(m_pBaseWindow->GetWindowHDC(), hPalette, TRUE); 
        SelectPalette(m_pBaseWindow->GetMemoryHDC(), hPalette, TRUE); 
    } 
 
    EXECUTE_ASSERT(DeleteObject(hPalOurs)); 
    m_hPalette = NULL; 
    return NOERROR; 
} 
 
 
// Called to create a palette for the object, the data structure used by GDI 
// to describe a palette is a LOGPALETTE, this includes a variable number of 
// PALETTEENTRY fields which are the colours, we have to convert the RGBQUAD 
// colour fields we are handed in a BITMAPINFO from the media type into these 
// This handles extraction of palettes from true colour and YUV media formats 
 
// We can be passed an optional device name if we wish to prepare a palette 
// for a specific monitor on a multi monitor system 
 
HPALETTE CImagePalette::MakePalette(const VIDEOINFOHEADER *pVideoInfo, LPSTR szDevice) 
{ 
    ASSERT(ContainsPalette(pVideoInfo) == TRUE); 
    ASSERT(pVideoInfo->bmiHeader.biClrUsed >= 0); 
    ASSERT(pVideoInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS); 
    BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo); 
 
    const RGBQUAD *pColours;            // Pointer to the palette 
    LOGPALETTE *lp;                     // Used to create a palette 
    HPALETTE hPalette;                  // Logical palette object 
 
    lp = (LOGPALETTE *) new BYTE[sizeof(LOGPALETTE) + SIZE_PALETTE]; 
    if (lp == NULL) { 
        return NULL; 
    } 
 
    // Unfortunately for some hare brained reason a GDI palette entry (a 
    // PALETTEENTRY structure) is different to a palette entry from a DIB 
    // format (a RGBQUAD structure) so we have to do the field conversion 
    // The VIDEOINFO containing the palette may be a true colour type so 
    // we use GetBitmapPalette to skip over any bit fields if they exist 
 
    lp->palVersion = PALVERSION; 
    lp->palNumEntries = (USHORT) pHeader->biClrUsed; 
    if (lp->palNumEntries == 0) lp->palNumEntries = (1 << pHeader->biBitCount); 
    pColours = GetBitmapPalette(pVideoInfo); 
 
    for (DWORD dwCount = 0;dwCount < lp->palNumEntries;dwCount++) { 
        lp->palPalEntry[dwCount].peRed = pColours[dwCount].rgbRed; 
        lp->palPalEntry[dwCount].peGreen = pColours[dwCount].rgbGreen; 
        lp->palPalEntry[dwCount].peBlue = pColours[dwCount].rgbBlue; 
        lp->palPalEntry[dwCount].peFlags = 0; 
    } 
 
    MakeIdentityPalette(lp->palPalEntry, lp->palNumEntries, szDevice); 
 
    // Create a logical palette 
 
    hPalette = CreatePalette(lp); 
    ASSERT(hPalette != NULL); 
    delete[] lp; 
    return hPalette; 
} 
 
 
// GDI does a fair job of compressing the palette entries you give it, so for 
// example if you have five entries with an RGB colour (0,0,0) it will remove 
// all but one of them. When you subsequently draw an image it will map from 
// your logical palette to the compressed device palette. This function looks 
// to see if it is trying to be an identity palette and if so sets the flags 
// field in the PALETTEENTRYs so they remain expanded to boost performance 
 
// We can be passed an optional device name if we wish to prepare a palette 
// for a specific monitor on a multi monitor system 
 
HRESULT CImagePalette::MakeIdentityPalette(PALETTEENTRY *pEntry,INT iColours, LPSTR szDevice) 
{ 
    PALETTEENTRY SystemEntries[10];         // System palette entries 
    BOOL bIdentityPalette = TRUE;           // Is an identity palette 
    ASSERT(iColours <= iPALETTE_COLORS);    // Should have a palette 
    const int PalLoCount = 10;              // First ten reserved colours 
    const int PalHiStart = 246;             // Last VGA palette entries 
 
    // Does this have the full colour range 
 
    if (iColours < 10) { 
        return S_FALSE; 
    } 
 
    // Apparently some displays have odd numbers of system colours 
 
    // Get a DC on the right monitor - it's ugly, but this is the way you have 
    // to do it 
    HDC hdc; 
    if (szDevice == NULL || lstrcmpiA(szDevice, "DISPLAY") == 0) 
        hdc = CreateDCA("DISPLAY", NULL, NULL, NULL); 
    else 
        hdc = CreateDCA(NULL, szDevice, NULL, NULL); 
    ASSERT(hdc); 
    INT Reserved = GetDeviceCaps(hdc,NUMRESERVED); 
    if (Reserved != 20) { 
        DeleteDC(hdc); 
        return S_FALSE; 
    } 
 
    // Compare our palette against the first ten system entries. The reason I 
    // don't do a memory compare between our two arrays of colours is because 
    // I am not sure what will be in the flags fields for the system entries 
 
    UINT Result = GetSystemPaletteEntries(hdc,0,PalLoCount,SystemEntries); 
    for (UINT Count = 0;Count < Result;Count++) { 
        if (SystemEntries[Count].peRed != pEntry[Count].peRed || 
                SystemEntries[Count].peGreen != pEntry[Count].peGreen || 
                    SystemEntries[Count].peBlue != pEntry[Count].peBlue) { 
                        bIdentityPalette = FALSE; 
        } 
    } 
 
    // And likewise compare against the last ten entries 
 
    Result = GetSystemPaletteEntries(hdc,PalHiStart,PalLoCount,SystemEntries); 
    for (Count = 0;Count < Result;Count++) { 
        if (INT(Count) + PalHiStart < iColours) { 
            if (SystemEntries[Count].peRed != pEntry[PalHiStart + Count].peRed || 
                    SystemEntries[Count].peGreen != pEntry[PalHiStart + Count].peGreen || 
                        SystemEntries[Count].peBlue != pEntry[PalHiStart + Count].peBlue) { 
                            bIdentityPalette = FALSE; 
            } 
        } 
    } 
 
    // If not an identity palette then return S_FALSE 
 
    DeleteDC(hdc); 
    if (bIdentityPalette == FALSE) { 
        return S_FALSE; 
    } 
 
    // Set the non VGA entries so that GDI doesn't map them 
 
    for (Count = PalLoCount;INT(Count) < min(PalHiStart,iColours);Count++) { 
        pEntry[Count].peFlags = PC_NOCOLLAPSE; 
    } 
    return NOERROR; 
} 
 
 
// Constructor initialises the VIDEOINFO we keep storing the current display 
// format. The format can be changed at any time, to reset the format held 
// by us call the RefreshDisplayType directly (it's a public method). Since 
// more than one thread will typically call us (ie window threads resetting 
// the type and source threads in the type checking methods) we have a lock 
 
CImageDisplay::CImageDisplay() 
{ 
    RefreshDisplayType(NULL); 
} 
 
 
 
// This initialises the format we hold which contains the display device type 
// We do a conversion on the display device type in here so that when we start 
// type checking input formats we can assume that certain fields have been set 
// correctly, an example is when we make the 16 bit mask fields explicit. This 
// is normally called when we receive WM_DEVMODECHANGED device change messages 
 
// The optional szDeviceName parameter tells us which monitor we are interested 
// in for a multi monitor system 
 
HRESULT CImageDisplay::RefreshDisplayType(LPSTR szDeviceName) 
{ 
    CAutoLock cDisplayLock(this); 
 
    // Set the preferred format type 
 
    ZeroMemory((PVOID)&m_Display,sizeof(VIDEOINFOHEADER)+sizeof(TRUECOLORINFO)); 
    m_Display.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    m_Display.bmiHeader.biBitCount = FALSE; 
 
    // Get the bit depth of a device compatible bitmap 
 
    // get caps of whichever monitor they are interested in (multi monitor) 
    HDC hdcDisplay; 
    // it's ugly, but this is the way you have to do it 
    if (szDeviceName == NULL || lstrcmpiA(szDeviceName, "DISPLAY") == 0) 
        hdcDisplay = CreateDCA("DISPLAY", NULL, NULL, NULL); 
    else 
        hdcDisplay = CreateDCA(NULL, szDeviceName, NULL, NULL); 
    if (hdcDisplay == NULL) { 
ASSERT(FALSE); 
DbgLog((LOG_ERROR,1,TEXT("ACK! Can't get a DC for %s"), 
szDeviceName ? szDeviceName : "<NULL>")); 
return E_FAIL; 
    } else { 
DbgLog((LOG_TRACE,3,TEXT("Created a DC for %s"), 
szDeviceName ? szDeviceName : "<NULL>")); 
    } 
    HBITMAP hbm = CreateCompatibleBitmap(hdcDisplay,1,1); 
    GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS); 
 
    // This call will get the colour table or the proper bitfields 
    GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS); 
    DeleteObject(hbm); 
    DeleteDC(hdcDisplay); 
 
    // Complete the display type initialisation 
 
    ASSERT(CheckHeaderValidity(&m_Display)); 
    UpdateFormat(&m_Display); 
    DbgLog((LOG_TRACE,3,TEXT("New DISPLAY bit depth =%d"), 
m_Display.bmiHeader.biBitCount)); 
    return NOERROR; 
} 
 
 
// We assume throughout this code that any bitfields masks are allowed no 
// more than eight bits to store a colour component. This checks that the 
// bit count assumption is enforced and also makes sure that all the bits 
// set are contiguous. We return a boolean TRUE if the field checks out ok 
 
BOOL CImageDisplay::CheckBitFields(const VIDEOINFO *pInput) 
{ 
    DWORD *pBitFields = (DWORD *) BITMASKS(pInput); 
 
    for (INT iColour = iRED;iColour <= iBLUE;iColour++) { 
 
        // First of all work out how many bits are set 
 
        DWORD SetBits = CountSetBits(pBitFields[iColour]); 
        if (SetBits > iMAXBITS || SetBits == 0) { 
            NOTE1("Bit fields for component %d invalid",iColour); 
            return FALSE; 
        } 
 
        // Next work out the number of zero bits prefix 
        DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]); 
 
        // This is going to see if all the bits set are contiguous (as they 
        // should be). We know how much to shift them right by from the 
        // count of prefix bits. The number of bits set defines a mask, we 
        // invert this (ones complement) and AND it with the shifted bit 
        // fields. If the result is NON zero then there are bit(s) sticking 
        // out the left hand end which means they are not contiguous 
 
        DWORD TestField = pBitFields[iColour] >> PrefixBits; 
        DWORD Mask = ULONG_MAX << SetBits; 
        if (TestField & Mask) { 
            NOTE1("Bit fields for component %d not contiguous",iColour); 
            return FALSE; 
        } 
    } 
    return TRUE; 
} 
 
 
// This counts the number of bits set in the input field 
 
DWORD CImageDisplay::CountSetBits(DWORD Field) 
{ 
    // This is a relatively well known bit counting algorithm 
 
    DWORD Count = 0; 
    DWORD init = Field; 
 
    // Until the input is exhausted, count the number of bits 
 
    while (init) { 
        init = init & (init - 1);  // Turn off the bottommost bit 
        Count++; 
    } 
    return Count; 
} 
 
 
// This counts the number of zero bits upto the first one set NOTE the input 
// field should have been previously checked to ensure there is at least one 
// set although if we don't find one set we return the impossible value 32 
 
DWORD CImageDisplay::CountPrefixBits(DWORD Field) 
{ 
    DWORD Mask = 1; 
    DWORD Count = 0; 
 
    while (TRUE) { 
        if (Field & Mask) { 
            return Count; 
        } 
        Count++; 
 
        ASSERT(Mask != 0x80000000); 
        if (Mask == 0x80000000) { 
            return Count; 
        } 
        Mask <<= 1; 
    } 
} 
 
 
// This is called to check the BITMAPINFOHEADER for the input type. There are 
// many implicit dependancies between the fields in a header structure which 
// if we validate now make for easier manipulation in subsequent handling. We 
// also check that the BITMAPINFOHEADER matches it's specification such that 
// fields likes the number of planes is one, that it's structure size is set 
// correctly and that the bitmap dimensions have not been set as negative 
 
BOOL CImageDisplay::CheckHeaderValidity(const VIDEOINFO *pInput) 
{ 
    // Check the bitmap width and height are not negative. 
 
    if (pInput->bmiHeader.biWidth <= 0 || 
pInput->bmiHeader.biHeight <= 0) { 
        NOTE("Invalid bitmap dimensions"); 
        return FALSE; 
    } 
 
    // Check the compression is either BI_RGB or BI_BITFIELDS 
 
    if (pInput->bmiHeader.biCompression != BI_RGB) { 
        if (pInput->bmiHeader.biCompression != BI_BITFIELDS) { 
            NOTE("Invalid compression format"); 
            return FALSE; 
        } 
    } 
 
    // If BI_BITFIELDS compression format check the colour depth 
 
    if (pInput->bmiHeader.biCompression == BI_BITFIELDS) { 
        if (pInput->bmiHeader.biBitCount != 16) { 
            if (pInput->bmiHeader.biBitCount != 32) { 
                NOTE("BI_BITFIELDS not 16/32 bit depth"); 
                return FALSE; 
            } 
        } 
    } 
 
    // Check the assumptions about the layout of the bit fields 
 
    if (pInput->bmiHeader.biCompression == BI_BITFIELDS) { 
        if (CheckBitFields(pInput) == FALSE) { 
            NOTE("Bit fields are not valid"); 
            return FALSE; 
        } 
    } 
 
    // Are the number of planes equal to one 
 
    if (pInput->bmiHeader.biPlanes != 1) { 
        NOTE("Number of planes not one"); 
        return FALSE; 
    } 
 
    // Check the image size is consistent (it can be zero) 
 
    if (pInput->bmiHeader.biSizeImage != GetBitmapSize(&pInput->bmiHeader)) { 
        if (pInput->bmiHeader.biSizeImage) { 
            NOTE("Image size incorrectly set"); 
            return FALSE; 
        } 
    } 
 
    // Check the size of the structure 
 
    if (pInput->bmiHeader.biSize != sizeof(BITMAPINFOHEADER)) { 
        NOTE("Size of BITMAPINFOHEADER wrong"); 
        return FALSE; 
    } 
    return CheckPaletteHeader(pInput); 
} 
 
 
// This runs a few simple tests against the palette fields in the input to 
// see if it looks vaguely correct. The tests look at the number of palette 
// colours present, the number considered important and the biCompression 
// field which should always be BI_RGB as no other formats are meaningful 
 
BOOL CImageDisplay::CheckPaletteHeader(const VIDEOINFO *pInput) 
{ 
    // The checks here are for palettised videos only 
 
    if (PALETTISED(pInput) == FALSE) { 
        if (pInput->bmiHeader.biClrUsed) { 
            NOTE("Invalid palette entries"); 
            return FALSE; 
        } 
        return TRUE; 
    } 
 
    // Compression type of BI_BITFIELDS is meaningless for palette video 
 
    if (pInput->bmiHeader.biCompression != BI_RGB) { 
        NOTE("Palettised video must be BI_RGB"); 
        return FALSE; 
    } 
 
    // Check the number of palette colours is correct 
 
    if (pInput->bmiHeader.biClrUsed > PALETTE_ENTRIES(pInput)) { 
        NOTE("Too many colours in palette"); 
        return FALSE; 
    } 
 
    // The number of important colours shouldn't exceed the number used 
 
    if (pInput->bmiHeader.biClrImportant > pInput->bmiHeader.biClrUsed) { 
        NOTE("Too many important colours"); 
        return FALSE; 
    } 
    return TRUE; 
} 
 
 
// Return the format of the video display 
 
const VIDEOINFO *CImageDisplay::GetDisplayFormat() 
{ 
    return &m_Display; 
} 
 
 
// Return TRUE if the display uses a palette 
 
BOOL CImageDisplay::IsPalettised() 
{ 
    return PALETTISED(&m_Display); 
} 
 
 
// Return the bit depth of the current display setting 
 
WORD CImageDisplay::GetDisplayDepth() 
{ 
    return m_Display.bmiHeader.biBitCount; 
} 
 
 
// Initialise the optional fields in a VIDEOINFO. These are mainly to do with 
// the source and destination rectangles and palette information such as the 
// number of colours present. It simplifies our code just a little if we don't 
// have to keep checking for all the different valid permutations in a header 
// every time we want to do anything with it (an example would be creating a 
// palette). We set the base class media type before calling this function so 
// that the media types between the pins match after a connection is made 
 
HRESULT CImageDisplay::UpdateFormat(VIDEOINFO *pVideoInfo) 
{ 
    ASSERT(pVideoInfo); 
 
    BITMAPINFOHEADER *pbmi = HEADER(pVideoInfo); 
    SetRectEmpty(&pVideoInfo->rcSource); 
    SetRectEmpty(&pVideoInfo->rcTarget); 
 
    // Set the number of colours explicitly 
 
    if (PALETTISED(pVideoInfo)) { 
        if (pVideoInfo->bmiHeader.biClrUsed == 0) { 
            pVideoInfo->bmiHeader.biClrUsed = PALETTE_ENTRIES(pVideoInfo); 
        } 
    } 
 
    // The number of important colours shouldn't exceed the number used, on 
    // some displays the number of important colours is not initialised when 
    // retrieving the display type so we set the colours used correctly 
 
    if (pVideoInfo->bmiHeader.biClrImportant > pVideoInfo->bmiHeader.biClrUsed) { 
        pVideoInfo->bmiHeader.biClrImportant = PALETTE_ENTRIES(pVideoInfo); 
    } 
 
    // Change the image size field to be explicit 
 
    if (pVideoInfo->bmiHeader.biSizeImage == 0) { 
        pVideoInfo->bmiHeader.biSizeImage = GetBitmapSize(&pVideoInfo->bmiHeader); 
    } 
    return NOERROR; 
} 
 
 
// Lots of video rendering filters want code to check proposed formats are ok 
// This checks the VIDEOINFO we are passed as a media type. If the media type 
// is a valid media type then we return NOERROR otherwise E_INVALIDARG. Note 
// however we only accept formats that can be easily displayed in the display 
// so if we are on a 16 bit device we will not accept 24 bit images. The one 
// complexity is that most displays draw 8 bit palettised images efficiently 
// Also if the input format is less colour bits per pixel then we also accept 
 
HRESULT CImageDisplay::CheckVideoType(const VIDEOINFO *pInput) 
{ 
    // First of all check the VIDEOINFOHEADER looks correct 
 
    if (CheckHeaderValidity(pInput) == FALSE) { 
        return E_INVALIDARG; 
    } 
 
    // Virtually all devices support palettised images efficiently 
 
    if (m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount) { 
        if (PALETTISED(pInput) == TRUE) { 
            ASSERT(PALETTISED(&m_Display) == TRUE); 
            NOTE("(Video) Type connection ACCEPTED"); 
            return NOERROR; 
        } 
    } 
 
    // Is the display depth greater than the input format 
 
    if (m_Display.bmiHeader.biBitCount > pInput->bmiHeader.biBitCount) { 
        NOTE("(Video) Mismatch agreed"); 
        return NOERROR; 
    } 
 
    // Is the display depth less than the input format 
 
    if (m_Display.bmiHeader.biBitCount < pInput->bmiHeader.biBitCount) { 
        NOTE("(Video) Format mismatch"); 
        return E_INVALIDARG; 
    } 
 
    // Both input and display formats are either BI_RGB or BI_BITFIELDS 
 
    ASSERT(m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount); 
    ASSERT(PALETTISED(pInput) == FALSE); 
    ASSERT(PALETTISED(&m_Display) == FALSE); 
 
    // BI_RGB 16 bit representation is implicitly RGB555, and likewise BI_RGB 
    // 24 bit representation is RGB888. So we initialise a pointer to the bit 
    // fields they really mean and check against the display device format 
    // This is only going to be called when both formats are equal bits pixel 
 
    const DWORD *pInputMask = GetBitMasks(pInput); 
    const DWORD *pDisplayMask = GetBitMasks((VIDEOINFO *)&m_Display); 
 
    if (pInputMask[iRED] != pDisplayMask[iRED] || 
            pInputMask[iGREEN] != pDisplayMask[iGREEN] || 
                pInputMask[iBLUE] != pDisplayMask[iBLUE]) { 
 
        NOTE("(Video) Bit field mismatch"); 
        return E_INVALIDARG; 
    } 
 
    NOTE("(Video) Type connection ACCEPTED"); 
    return NOERROR; 
} 
 
 
// Return the bit masks for the true colour VIDEOINFO provided 
 
const DWORD *CImageDisplay::GetBitMasks(const VIDEOINFO *pVideoInfo) 
{ 
    static const DWORD FailMasks[] = {0,0,0}; 
 
    if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) { 
        return BITMASKS(pVideoInfo); 
    } 
 
    ASSERT(pVideoInfo->bmiHeader.biCompression == BI_RGB); 
 
    switch (pVideoInfo->bmiHeader.biBitCount) { 
        case 16: return bits555; 
        case 24: return bits888; 
        case 32: return bits888; 
        default: return FailMasks; 
    } 
} 
 
 
// Check to see if we can support media type pmtIn as proposed by the output 
// pin - We first check that the major media type is video and also identify 
// the media sub type. Then we thoroughly check the VIDEOINFO type provided 
// As well as the contained VIDEOINFO being correct the major type must be 
// video, the subtype a recognised video format and the type GUID correct 
 
HRESULT CImageDisplay::CheckMediaType(const CMediaType *pmtIn) 
{ 
    // Does this have a VIDEOINFOHEADER format block 
 
    const GUID *pFormatType = pmtIn->FormatType(); 
    if (*pFormatType != FORMAT_VideoInfo) { 
        NOTE("Format GUID not a VIDEOINFOHEADER"); 
        return E_INVALIDARG; 
    } 
    ASSERT(pmtIn->Format()); 
 
    // Check the format looks reasonably ok 
 
    ULONG Length = pmtIn->FormatLength(); 
    if (Length < SIZE_VIDEOHEADER) { 
        NOTE("Format smaller than a VIDEOHEADER"); 
        return E_FAIL; 
    } 
 
    VIDEOINFO *pInput = (VIDEOINFO *) pmtIn->Format(); 
 
    // Check the major type is MEDIATYPE_Video 
 
    const GUID *pMajorType = pmtIn->Type(); 
    if (*pMajorType != MEDIATYPE_Video) { 
        NOTE("Major type not MEDIATYPE_Video"); 
        return E_INVALIDARG; 
    } 
 
    // Check we can identify the media subtype 
 
    const GUID *pSubType = pmtIn->Subtype(); 
    if (GetBitCount(pSubType) == USHRT_MAX) { 
        NOTE("Invalid video media subtype"); 
        return E_INVALIDARG; 
    } 
    return CheckVideoType(pInput); 
} 
 
 
// Given a video format described by a VIDEOINFO structure we return the mask 
// that is used to obtain the range of acceptable colours for this type, for 
// example, the mask for a 24 bit true colour format is 0xFF in all cases. A 
// 16 bit 5:6:5 display format uses 0xF8, 0xFC and 0xF8, therefore given any 
// RGB triplets we can AND them with these fields to find one that is valid 
 
BOOL CImageDisplay::GetColourMask(DWORD *pMaskRed, 
                                  DWORD *pMaskGreen, 
                                  DWORD *pMaskBlue) 
{ 
    CAutoLock cDisplayLock(this); 
    *pMaskRed = 0xFF; 
    *pMaskGreen = 0xFF; 
    *pMaskBlue = 0xFF; 
 
    // If this format is palettised then it doesn't have bit fields 
 
    if (m_Display.bmiHeader.biBitCount < 16) { 
        return FALSE; 
    } 
 
    // If this is a 24 bit true colour display then it can handle all the 
    // possible colour component ranges described by a byte. It is never 
    // allowed for a 24 bit colour depth image to have BI_BITFIELDS set 
 
    if (m_Display.bmiHeader.biBitCount == 24) { 
        ASSERT(m_Display.bmiHeader.biCompression == BI_RGB); 
        return TRUE; 
    } 
 
    // Calculate the mask based on the format's bit fields 
 
    const DWORD *pBitFields = (DWORD *) GetBitMasks((VIDEOINFO *)&m_Display); 
    DWORD *pOutputMask[] = { pMaskRed, pMaskGreen, pMaskBlue }; 
 
    // We know from earlier testing that there are no more than iMAXBITS 
    // bits set in the mask and that they are all contiguous. All that 
    // therefore remains is to shift them into the correct position 
 
    for (INT iColour = iRED;iColour <= iBLUE;iColour++) { 
 
        // This works out how many bits there are and where they live 
 
        DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]); 
        DWORD SetBits = CountSetBits(pBitFields[iColour]); 
 
        // The first shift moves the bit field so that it is right justified 
        // in the DWORD, after which we then shift it back left which then 
        // puts the leading bit in the bytes most significant bit position 
 
        *(pOutputMask[iColour]) = pBitFields[iColour] >> PrefixBits; 
        *(pOutputMask[iColour]) <<= (iMAXBITS - SetBits); 
    } 
    return TRUE; 
}