home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / mac / SiteBldr / AMOVIE / SDK / _SETUP / COMMON.Z / winutil.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-13  |  82.2 KB  |  2,375 lines

  1. //==========================================================================;
  2. //
  3. //  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4. //  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5. //  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6. //  PURPOSE.
  7. //
  8. //  Copyright (c) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
  9. //
  10. //--------------------------------------------------------------------------;
  11.  
  12. // Generic window handler base class, December 1995
  13.  
  14. #include <streams.h>
  15. #include <limits.h>
  16.  
  17.  
  18. // Initialise the under construction object pointer
  19. CBaseWindow *CBaseWindow::s_pInitBaseWindow = NULL;
  20.  
  21.  
  22. // Constructor
  23.  
  24. CBaseWindow::CBaseWindow(void) :
  25.     m_hInstance(g_hInst),
  26.     m_hThread(INVALID_HANDLE_VALUE),
  27.     m_ThreadID(0),
  28.     m_hwnd(NULL),
  29.     m_hdc(NULL),
  30.     m_bActivated(FALSE),
  31.     m_pClassName(NULL),
  32.     m_ClassStyles(0),
  33.     m_WindowStyles(0),
  34.     m_WindowStylesEx(0),
  35.     m_ThreadSignal(NOERROR),
  36.     m_GoodByeMessage(0),
  37.     m_ShowStageMessage(0),
  38.     m_MemoryDC(NULL),
  39.     m_hPalette(NULL),
  40.     m_bBackground(FALSE)
  41. {
  42. }
  43.  
  44.  
  45. // Prepare a window by spinning off a worker thread to do the creation and
  46. // also poll the message input queue. We leave this to be called by derived
  47. // classes because they might want to override methods like MessageLoop and
  48. // InitialiseWindow, if we do this during construction they'll ALWAYS call
  49. // this base class methods. We make the worker thread create the window so
  50. // it owns it rather than the filter graph thread which is constructing us
  51.  
  52. HRESULT CBaseWindow::PrepareWindow()
  53. {
  54.     ASSERT(m_hwnd == NULL);
  55.     ASSERT(m_hdc == NULL);
  56.     m_ThreadSignal = NOERROR;
  57.  
  58.     // Get the derived object's window and class styles
  59.  
  60.     m_pClassName = GetClassWindowStyles(&m_ClassStyles,
  61.                                         &m_WindowStyles,
  62.                                         &m_WindowStylesEx);
  63.     if (m_pClassName == NULL) {
  64.         return E_FAIL;
  65.     }
  66.  
  67.     // Register our special private messages
  68.  
  69.     m_GoodByeMessage = RegisterWindowMessage(m_pClassName);
  70.     m_ShowStageMessage = RegisterWindowMessage(SHOWSTAGE);
  71.  
  72.     // Create a thread to look after the window
  73.  
  74.     m_hThread = CreateThread(NULL,                  // Security attributes
  75.                              (DWORD) 0,             // Initial stack size
  76.                              WindowMessageLoop,     // Thread start address
  77.                              (LPVOID) this,         // Thread parameter
  78.                              (DWORD) 0,             // Creation flags
  79.                              &m_ThreadID);          // Thread identifier
  80.  
  81.     // If we couldn't create a thread the whole thing's off
  82.  
  83.     ASSERT(m_hThread);
  84.     if (m_hThread == NULL) {
  85.         return E_FAIL;
  86.     }
  87.  
  88.     // Wait until the window has been initialised
  89.     m_SyncWorker.Wait();
  90.  
  91.     // The thread needs to grab some resources when it is created, these may
  92.     // or may not be available. If they are not then the thread fills in the
  93.     // m_ThreadSignal and signals the constructor event to complete and then
  94.     // exits. We test here to see if the construction completed ok and if not
  95.     // we have ourselves destroyed which will close the open thread handle
  96.  
  97.     return m_ThreadSignal;
  98. }
  99.  
  100.  
  101. // Destructor just a placeholder so that we know it becomes virtual
  102. // Derived classes MUST call DoneWithWindow in their destructors so
  103. // that no messages arrive after the derived class constructor ends
  104.  
  105. CBaseWindow::~CBaseWindow()
  106. {
  107.     ASSERT(m_hwnd == NULL);
  108.     ASSERT(m_hdc == NULL);
  109. }
  110.  
  111.  
  112. // Wait for the window thread to complete by sending a custom message - we
  113. // found that if a user has full drag on and is dragging the window at the
  114. // same time as the application destroys the renderer the m_GoodByeMessage
  115. // can be processed which means the worker thread calls UninitialiseWindow
  116. // and then PostQuitMessage, but the final WM_QUIT does not get send. The
  117. // solution is to save the window handle to keep sending it until it quits
  118.  
  119. HRESULT CBaseWindow::DoneWithWindow()
  120. {
  121.     if (m_hwnd == NULL) {
  122.         return NOERROR;
  123.     }
  124.  
  125.     HWND hwnd = m_hwnd;
  126.     InactivateWindow();
  127.     NOTE("Inactivated");
  128.  
  129.     // Reset the window styles before destruction
  130.  
  131.     SetWindowLong(m_hwnd,GWL_STYLE,m_WindowStyles);
  132.     ASSERT(GetParent(m_hwnd) == NULL);
  133.     NOTE1("Reset window styles %d",m_WindowStyles);
  134.  
  135.     // Tell the worker thread to destroy the window
  136.  
  137.     PostMessage(hwnd,m_GoodByeMessage,(WPARAM) 0,(LPARAM) 0);
  138.     DWORD dwWait;
  139.     for (;;) {
  140.  
  141.         dwWait = MsgWaitForMultipleObjects(1,&m_hThread,FALSE,500,QS_SENDMESSAGE);
  142.         if (dwWait == WAIT_OBJECT_0) {
  143.             break;
  144.         } else if (dwWait == WAIT_TIMEOUT) {
  145.             NOTE("Waiting for thread termination got WAIT_TIMEOUT");
  146.             PostMessage(hwnd,m_GoodByeMessage,(WPARAM) 0,(LPARAM) 0);
  147.         } else if (dwWait == WAIT_OBJECT_0 + 1) {
  148.             // interthread sendmessage - to process it, we need to look at the
  149.             // message queue
  150.             MSG msg;
  151.             PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  152.         }
  153.     }
  154.     EXECUTE_ASSERT(CloseHandle(m_hThread));
  155.  
  156.     // Reset our state so we can be prepared again
  157.  
  158.     m_hThread = INVALID_HANDLE_VALUE;
  159.     m_ThreadID = 0;
  160.     m_pClassName = NULL;
  161.     m_ClassStyles = 0;
  162.     m_WindowStyles = 0;
  163.     m_WindowStylesEx = 0;
  164.     m_GoodByeMessage = 0;
  165.     m_ShowStageMessage = 0;
  166.  
  167.     return NOERROR;
  168. }
  169.  
  170.  
  171. // Called at the end to put the window in an inactive state. The pending list
  172. // will always have been cleared by this time so event if the worker thread
  173. // gets has been signaled and gets in to render something it will find both
  174. // the state has been changed and that there are no available sample images
  175. // Since we wait on the window thread to complete we don't lock the object
  176.  
  177. HRESULT CBaseWindow::InactivateWindow()
  178. {
  179.     // Has the window been activated
  180.     if (m_bActivated == FALSE) {
  181.         return S_FALSE;
  182.     }
  183.  
  184.     m_bActivated = FALSE;
  185.     ShowWindow(m_hwnd,SW_HIDE);
  186.     return NOERROR;
  187. }
  188.  
  189.  
  190. // This displays a normal window. We ask the base window class for default
  191. // sizes which unless overriden will return DEFWIDTH and DEFHEIGHT. We go
  192. // through a couple of extra hoops to get the client area the right size
  193. // as the object specifies which accounts for the AdjustWindowRectEx calls
  194. // We also DWORD align the left and top coordinates of the window here to
  195. // maximise the chance of being able to use DCI/DirectDraw primary surface
  196.  
  197. HRESULT CBaseWindow::ActivateWindow()
  198. {
  199.     // Has the window been sized and positioned already
  200.  
  201.     if (m_bActivated == TRUE || GetParent(m_hwnd) != NULL) {
  202.  
  203.         SetWindowPos(m_hwnd,            // Our window handle
  204.                      HWND_TOP,          // Put it at the top
  205.                      0, 0, 0, 0,        // Leave in current position
  206.                      SWP_NOMOVE |       // Don't change it's place
  207.                      SWP_NOSIZE);       // Change Z-order only
  208.  
  209.         return S_FALSE;
  210.     }
  211.  
  212.     // Calculate the desired client rectangle
  213.  
  214.     RECT WindowRect, ClientRect = GetDefaultRect();
  215.     GetWindowRect(m_hwnd,&WindowRect);
  216.     AdjustWindowRectEx(&ClientRect,GetWindowLong(m_hwnd,GWL_STYLE),
  217.                        FALSE,GetWindowLong(m_hwnd,GWL_EXSTYLE));
  218.  
  219.     // Align left and top edges on DWORD boundaries
  220.  
  221.     UINT WindowFlags = (SWP_NOACTIVATE | SWP_FRAMECHANGED);
  222.     WindowRect.left -= (WindowRect.left & 3);
  223.     WindowRect.top -= (WindowRect.top & 3);
  224.  
  225.     SetWindowPos(m_hwnd,                // Window handle
  226.                  HWND_TOP,              // Put it at the top
  227.                  WindowRect.left,       // Align left edge
  228.                  WindowRect.top,        // And also top place
  229.                  WIDTH(&ClientRect),    // Horizontal size
  230.                  HEIGHT(&ClientRect),   // Vertical size
  231.                  WindowFlags);          // Don't show window
  232.  
  233.     m_bActivated = TRUE;
  234.     return NOERROR;
  235. }
  236.  
  237.  
  238. // This can be used to DWORD align the window for maximum performance
  239.  
  240. HRESULT CBaseWindow::PerformanceAlignWindow()
  241. {
  242.     RECT ClientRect,WindowRect;
  243.     GetWindowRect(m_hwnd,&WindowRect);
  244.     ASSERT(m_bActivated == TRUE);
  245.  
  246.     // Don't do this if we're owned
  247.  
  248.     if (GetParent(m_hwnd)) {
  249.         return NOERROR;
  250.     }
  251.  
  252.     // Align left and top edges on DWORD boundaries
  253.  
  254.     GetClientRect(m_hwnd, &ClientRect);
  255.     MapWindowPoints(m_hwnd, HWND_DESKTOP, (LPPOINT) &ClientRect, 2);
  256.     WindowRect.left -= (ClientRect.left & 3);
  257.     WindowRect.top  -= (ClientRect.top  & 3);
  258.     UINT WindowFlags = (SWP_NOACTIVATE | SWP_NOSIZE);
  259.  
  260.     SetWindowPos(m_hwnd,                // Window handle
  261.                  HWND_TOP,              // Put it at the top
  262.                  WindowRect.left,       // Align left edge
  263.                  WindowRect.top,        // And also top place
  264.                  (int) 0,(int) 0,       // Ignore these sizes
  265.                  WindowFlags);          // Don't show window
  266.  
  267.     return NOERROR;
  268. }
  269.  
  270.  
  271. // Install a palette into the base window - we may be called by a different
  272. // thread to the one that owns the window. Therefore we cannot realise our
  273. // palettes in this function (or called functions) as it may cause an inter
  274. // thread send message to our window thread. Therefore rather than sending
  275. // a WM_QUERYNEWPALETTE to the window to realise the palette we'll post it
  276.  
  277. HRESULT CBaseWindow::SetPalette(HPALETTE hPalette)
  278. {
  279.     // We must own the window lock during the change
  280.  
  281.     CAutoLock cWindowLock(&m_WindowLock);
  282.     ASSERT(hPalette);
  283.     m_hPalette = hPalette;
  284.  
  285.     // Select the palette into the device contexts
  286.  
  287.     SelectPalette(m_hdc,m_hPalette,m_bBackground);
  288.     SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);
  289.     PostMessage(m_hwnd,WM_QUERYNEWPALETTE,0,0);
  290.  
  291.     return NOERROR;
  292. }
  293.  
  294.  
  295. // Realise our palettes in the window and device contexts
  296.  
  297. HRESULT CBaseWindow::DoRealisePalette()
  298. {
  299.     CAutoLock cWindowLock(&m_WindowLock);
  300.     if (m_hPalette == NULL) {
  301.         return NOERROR;
  302.     }
  303.  
  304.     // Realize on the window thread
  305.  
  306.     SelectPalette(m_hdc,m_hPalette,m_bBackground);
  307.     RealizePalette(m_hdc);
  308.     SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);
  309.     RealizePalette(m_MemoryDC);
  310.     GdiFlush();
  311.     return NOERROR;
  312. }
  313.  
  314.  
  315. // This is the global window procedure
  316.  
  317. LRESULT CALLBACK WndProc(HWND hwnd,         // Window handle
  318.                          UINT uMsg,         // Message ID
  319.                          WPARAM wParam,     // First parameter
  320.                          LPARAM lParam)     // Other parameter
  321. {
  322.     // Get the window long that holds our window object pointer
  323.     // If it is NULL then we are initialising the window in which
  324.     // case the object pointer is stored in static class variable
  325.  
  326.     CBaseWindow *pBaseWindow = (CBaseWindow *)GetWindowLong(hwnd,0);
  327.     if (pBaseWindow == NULL) {
  328.         ASSERT(CBaseWindow::s_pInitBaseWindow);
  329.         pBaseWindow = CBaseWindow::s_pInitBaseWindow;
  330.     }
  331.     return pBaseWindow->OnReceiveMessage(hwnd,uMsg,wParam,lParam);
  332. }
  333.  
  334.  
  335. // When the window size changes we adjust our member variables that
  336. // contain the dimensions of the client rectangle for our window so
  337. // that we come to render an image we will know whether to stretch
  338.  
  339. BOOL CBaseWindow::OnSize(LONG Width, LONG Height)
  340. {
  341.     m_Width = Width;
  342.     m_Height = Height;
  343.     return TRUE;
  344. }
  345.  
  346.  
  347. // This function handles the WM_CLOSE message
  348.  
  349. BOOL CBaseWindow::OnClose()
  350. {
  351.     ShowWindow(m_hwnd,SW_HIDE);
  352.     return TRUE;
  353. }
  354.  
  355.  
  356. // This is called by the worker window thread when it receives a terminate
  357. // message from the window object destructor to delete all the resources we
  358. // allocated during initialisation. By the time the worker thread exits all
  359. // processing will have been completed as the source filter disconnection
  360. // flushes the image pending sample, therefore the GdiFlush should succeed
  361.  
  362. HRESULT CBaseWindow::UninitialiseWindow()
  363. {
  364.     // Have we already cleaned up
  365.  
  366.     if (m_hwnd == NULL) {
  367.         ASSERT(m_hdc == NULL);
  368.         ASSERT(m_MemoryDC == NULL);
  369.         return NOERROR;
  370.     }
  371.  
  372.     // Release the window resources
  373.  
  374.     EXECUTE_ASSERT(GdiFlush());
  375.     EXECUTE_ASSERT(ReleaseDC(m_hwnd,m_hdc));
  376.     EXECUTE_ASSERT(DeleteDC(m_MemoryDC));
  377.  
  378.     // Reset the window variables
  379.  
  380.     m_hdc = NULL;
  381.     m_hwnd = NULL;
  382.     m_MemoryDC = NULL;
  383.     return NOERROR;
  384. }
  385.  
  386.  
  387. // This is called by the worker window thread after it has created the main
  388. // window and it wants to initialise the rest of the owner objects window
  389. // variables such as the device contexts. We execute this function with the
  390. // critical section still locked. Nothing in this function must generate any
  391. // SendMessage calls to the window because this is executing on the window
  392. // thread so the message will never be processed and we will deadlock
  393.  
  394. HRESULT CBaseWindow::InitialiseWindow(HWND hwnd)
  395. {
  396.     // Initialise the window variables
  397.  
  398.     m_hwnd = hwnd;
  399.     m_hdc = GetDC(hwnd);
  400.     m_MemoryDC = CreateCompatibleDC(m_hdc);
  401.  
  402.     SetStretchBltMode(m_hdc,COLORONCOLOR);
  403.     SetStretchBltMode(m_MemoryDC,COLORONCOLOR);
  404.  
  405.     // Quick sanity check
  406.  
  407.     ASSERT(m_hdc != NULL);
  408.     return NOERROR;
  409. }
  410.  
  411.  
  412. // This is the windows message loop for our worker thread. It does a loop
  413. // processing and dispatching messages until it receives a WM_QUIT message
  414. // which will normally be generated through the owning object's destructor
  415.  
  416. HRESULT CBaseWindow::MessageLoop()
  417. {
  418.     MSG Message;
  419.  
  420.     while (GetMessage(&Message,NULL,0,0))
  421.     {
  422.         TranslateMessage(&Message);
  423.         DispatchMessage(&Message);
  424.     }
  425.     return NOERROR;
  426. }
  427.  
  428.  
  429. // This creates a normal window and processes messages on a separate thread.
  430. // We use a mutex object that serialises window creation globally so we can
  431. // ensure that odd timing problems do not occur when registering the window.
  432. // We would still need to serialise access to the s_pInitVideoWindow class
  433. // field (albeit only in each process) because it is static. During window
  434. // construction we set the object pointer that owns the window in a global
  435. // variable so that all the window messages that arrive at the input queue
  436. // can be passed onto the correct object. Once the window has been created
  437. // we put the this pointer into a window LONG field where it is retrieved
  438. // in the message procedure. The constructor for the window class must be
  439. // signalled when we can let them continue, this is done through a member
  440. // variable (m_SyncWorker) which we can access as we are a friend method
  441.  
  442. DWORD __stdcall CBaseWindow::WindowMessageLoop(LPVOID lpvThreadParm)
  443. {
  444.     CBaseWindow *pBaseWindow;           // The owner window object
  445.     HANDLE WindowMutex;                 // Serialises object creation
  446.     WNDCLASS wndclass;                  // Used to register classes
  447.     BOOL bRegistered;                   // Is this class registered
  448.     HWND hwnd;                          // Handle to our window
  449.  
  450.     // Cast the thread parameter to be our owner object
  451.     pBaseWindow = (CBaseWindow *) lpvThreadParm;
  452.  
  453.     // Create/open the window mutex object
  454.  
  455.     WindowMutex = CreateMutex(NULL,FALSE,pBaseWindow->m_pClassName);
  456.     if (WindowMutex == NULL) {
  457.         DWORD Error = GetLastError();
  458.         pBaseWindow->m_ThreadSignal = HRESULT_FROM_WIN32(Error);
  459.         pBaseWindow->m_SyncWorker.Set();
  460.         ExitThread(FALSE);
  461.     }
  462.  
  463.     WaitForSingleObject(WindowMutex,INFINITE);
  464.     CBaseWindow::s_pInitBaseWindow = pBaseWindow;
  465.  
  466.     bRegistered = GetClassInfo(pBaseWindow->m_hInstance,   // Module instance
  467.                                pBaseWindow->m_pClassName,  // Window class
  468.                                &wndclass);                 // Info structure
  469.  
  470.     if (bRegistered == FALSE) {
  471.  
  472.         // Register the renderer window class
  473.  
  474.         wndclass.lpszClassName = pBaseWindow->m_pClassName;
  475.         wndclass.style         = pBaseWindow->m_ClassStyles;
  476.         wndclass.lpfnWndProc   = (WNDPROC) WndProc;
  477.         wndclass.cbClsExtra    = 0;
  478.         wndclass.cbWndExtra    = sizeof(CBaseWindow *);
  479.         wndclass.hInstance     = pBaseWindow->m_hInstance;
  480.         wndclass.hIcon         = NULL;
  481.         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
  482.         wndclass.hbrBackground = (HBRUSH) NULL;
  483.         wndclass.lpszMenuName  = NULL;
  484.  
  485.         RegisterClass(&wndclass);
  486.     }
  487.  
  488.     // Create the frame window
  489.  
  490.     hwnd = CreateWindowEx(pBaseWindow->m_WindowStylesEx,  // Extended styles
  491.                           pBaseWindow->m_pClassName,      // Registered name
  492.                           TEXT("ActiveMovie Window"),     // Window title
  493.                           pBaseWindow->m_WindowStyles,    // Window styles
  494.                           CW_USEDEFAULT,                  // Start x position
  495.                           CW_USEDEFAULT,                  // Start y position
  496.                           DEFWIDTH,                       // Window width
  497.                           DEFHEIGHT,                      // Window height
  498.                           NULL,                           // Parent handle
  499.                           NULL,                           // Menu handle
  500.                           pBaseWindow->m_hInstance,       // Instance handle
  501.                           &pBaseWindow);                  // Creation data
  502.  
  503.     // If we failed signal an error to the object constructor (based on the
  504.     // last Win32 error on this thread) then signal the constructor thread
  505.     // to continue, release the mutex to let others have a go and exit
  506.  
  507.     if (hwnd == NULL) {
  508.         DWORD Error = GetLastError();
  509.         pBaseWindow->m_ThreadSignal = HRESULT_FROM_WIN32(Error);
  510.         pBaseWindow->m_SyncWorker.Set();
  511.         EXECUTE_ASSERT(ReleaseMutex(WindowMutex));
  512.         EXECUTE_ASSERT(CloseHandle(WindowMutex));
  513.         ExitThread(FALSE);
  514.     }
  515.  
  516.     // Set the window LONG to be the object who created us
  517.     SetWindowLong(hwnd, (DWORD) 0, (LONG) pBaseWindow);
  518.     pBaseWindow->s_pInitBaseWindow = NULL;
  519.  
  520.     // Initialise the window and then signal the constructor so that it can
  521.     // continue and then finally unlock the object's critical section. The
  522.     // window class is left registered even after we terminate the thread
  523.     // as we don't know when the last window has been closed. So we allow
  524.     // the operating system to free the class resources as appropriate
  525.  
  526.     pBaseWindow->InitialiseWindow(hwnd);
  527.     pBaseWindow->m_SyncWorker.Set();
  528.     EXECUTE_ASSERT(ReleaseMutex(WindowMutex));
  529.     EXECUTE_ASSERT(CloseHandle(WindowMutex));
  530.  
  531.     pBaseWindow->MessageLoop();
  532.     ExitThread(TRUE);
  533.     return TRUE;
  534. }
  535.  
  536.  
  537. // The base class provides some default handling and calls DefWindowProc
  538.  
  539. LRESULT CBaseWindow::OnReceiveMessage(HWND hwnd,         // Window handle
  540.                                       UINT uMsg,         // Message ID
  541.                                       WPARAM wParam,     // First parameter
  542.                                       LPARAM lParam)     // Other parameter
  543. {
  544.     ASSERT(IsWindow(hwnd));
  545.  
  546.     // We receive a terminate window message (synchronously) from the
  547.     // window object destructor in which case we do actually destroy
  548.     // the window and complete the process in the WM_DESTROY message
  549.  
  550.     if (uMsg == m_GoodByeMessage) {
  551.         DestroyWindow(m_hwnd);
  552.         PostQuitMessage(FALSE);
  553.         return (LRESULT) 0;
  554.     }
  555.  
  556.     // This is sent by the IVideoWindow SetWindowForeground method. If the
  557.     // window is invisible we will show it and make it topmost without the
  558.     // foreground focus. If the window is visible it will also be made the
  559.     // topmost window without the foreground focus. If wParam is TRUE then
  560.     // for both cases the window will be forced into the foreground focus
  561.  
  562.     if (uMsg == m_ShowStageMessage) {
  563.  
  564.         BOOL bVisible = IsWindowVisible(hwnd);
  565.         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
  566.                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW |
  567.                      (bVisible ? SWP_NOACTIVATE : 0));
  568.  
  569.         // Should we bring the window to the foreground
  570.         if (wParam == TRUE) {
  571.             SetForegroundWindow(hwnd);
  572.         }
  573.         return (LRESULT) 1;
  574.     }
  575.  
  576.     switch (uMsg) {
  577.  
  578.         // Repaint the window if the system colours change
  579.  
  580.         case WM_SYSCOLORCHANGE:
  581.  
  582.             InvalidateRect(hwnd,NULL,FALSE);
  583.             return (LRESULT) 1;
  584.  
  585.         // Somebody is or has changed the palette
  586.  
  587.         case WM_PALETTECHANGED:
  588.         case WM_PALETTEISCHANGING:
  589.  
  590.             OnPaletteChange((HWND)wParam,uMsg);
  591.             return (LRESULT) 0;
  592.  
  593.         // We are about to receive the keyboard focus so we ask GDI to realise
  594.         // our logical palette again and hopefully it will be fully installed
  595.         // without any mapping having to be done during any picture rendering
  596.  
  597.         case WM_QUERYNEWPALETTE:
  598.  
  599.             return OnPaletteChange(m_hwnd,uMsg);
  600.  
  601.         // Store the width and height as useful base class members
  602.  
  603.         case WM_SIZE:
  604.  
  605.         OnSize(LOWORD(lParam), HIWORD(lParam));
  606.             return (LRESULT) 0;
  607.  
  608.         // Intercept the WM_CLOSE messages to hide the window
  609.  
  610.         case WM_CLOSE:
  611.  
  612.             OnClose();
  613.             return (LRESULT) 0;
  614.  
  615.         // Post the final WM_QUIT message to the window queue
  616.  
  617.         case WM_DESTROY:
  618.  
  619.             UninitialiseWindow();
  620.             PostQuitMessage(FALSE);
  621.             return (LRESULT) 0;
  622.     }
  623.     return DefWindowProc(hwnd,uMsg,wParam,lParam);
  624. }
  625.  
  626.  
  627. // This handles the palette change messages, if we do realise our palette then
  628. // we return TRUE otherwise we return FALSE. If we are the current foreground
  629. // window then we get first choice in the system palette entries although it
  630. // doesn't guarantee that our requirements will be met. Best performance is
  631. // obtained when our logical palette includes the standard VGA colours as it
  632. // virtually ensures any image will not have to be mapped during display. We
  633. // first check that the window handle is non NULL, if not then we install our
  634. // and realize our palettes again (by calling our DoRealisePalette method)
  635.  
  636. LRESULT CBaseWindow::OnPaletteChange(HWND hwnd,UINT Message)
  637. {
  638.     // First check we are not changing the palette during closedown
  639.  
  640.     if (m_hwnd == NULL || hwnd == NULL) {
  641.         return (LRESULT) 0;
  642.     }
  643.  
  644.     // Should we realise our palette again
  645.  
  646.     if (Message == WM_QUERYNEWPALETTE || hwnd != m_hwnd) {
  647.         DoRealisePalette();
  648.     }
  649.  
  650.     // Should we redraw the window with the new palette
  651.  
  652.     if (Message == WM_PALETTECHANGED) {
  653.         InvalidateRect(m_hwnd,NULL,FALSE);
  654.     }
  655.     return (LRESULT) 1;
  656. }
  657.  
  658.  
  659. // Return the default window rectangle
  660.  
  661. RECT CBaseWindow::GetDefaultRect()
  662. {
  663.     RECT DefaultRect = {0,0,DEFWIDTH,DEFHEIGHT};
  664.     ASSERT(m_hwnd);
  665.     ASSERT(m_hdc);
  666.     return DefaultRect;
  667. }
  668.  
  669.  
  670. // Return the current window width
  671.  
  672. LONG CBaseWindow::GetWindowWidth()
  673. {
  674.     ASSERT(m_hwnd);
  675.     ASSERT(m_hdc);
  676.     return m_Width;
  677. }
  678.  
  679.  
  680. // Return the current window height
  681.  
  682. LONG CBaseWindow::GetWindowHeight()
  683. {
  684.     ASSERT(m_hwnd);
  685.     ASSERT(m_hdc);
  686.     return m_Height;
  687. }
  688.  
  689.  
  690. // Return the window handle
  691.  
  692. HWND CBaseWindow::GetWindowHWND()
  693. {
  694.     ASSERT(m_hwnd);
  695.     ASSERT(m_hdc);
  696.     return m_hwnd;
  697. }
  698.  
  699.  
  700. // Return the window drawing device context
  701.  
  702. HDC CBaseWindow::GetWindowHDC()
  703. {
  704.     ASSERT(m_hwnd);
  705.     ASSERT(m_hdc);
  706.     return m_hdc;
  707. }
  708.  
  709.  
  710. // Return the offscreen window drawing device context
  711.  
  712. HDC CBaseWindow::GetMemoryHDC()
  713. {
  714.     ASSERT(m_hwnd);
  715.     ASSERT(m_MemoryDC);
  716.     return m_MemoryDC;
  717. }
  718.  
  719.  
  720. // This is available to clients who want to change the window visiblity. It's
  721. // little more than an indirection to the Win32 ShowWindow although these is
  722. // some benefit in going through here as this function may change sometime
  723.  
  724. HRESULT CBaseWindow::DoShowWindow(LONG ShowCmd)
  725. {
  726.     ShowWindow(m_hwnd,ShowCmd);
  727.     return NOERROR;
  728. }
  729.  
  730.  
  731. // Generate a WM_PAINT message for the video window
  732.  
  733. void CBaseWindow::PaintWindow(BOOL bErase)
  734. {
  735.     InvalidateRect(m_hwnd,NULL,bErase);
  736. }
  737.  
  738.  
  739. // Allow an application to have us set the video window in the foreground. We
  740. // have this because it is difficult for one thread to do do this to a window
  741. // owned by another thread. Rather than expose the message we use to execute
  742. // the inter thread send message we provide the interface function. All we do
  743. // is to SendMessage to the video window renderer thread with a WM_SHOWSTAGE
  744.  
  745. void CBaseWindow::DoSetWindowForeground(BOOL bFocus)
  746. {
  747.     SendMessage(m_hwnd,m_ShowStageMessage,(WPARAM) bFocus,(LPARAM) 0);
  748. }
  749.  
  750.  
  751. // Constructor initialises the owning object pointer. Since we are a worker
  752. // class for the main window object we have relatively few state variables to
  753. // look after. We are given device context handles to use later on as well as
  754. // the source and destination rectangles (but reset them here just in case)
  755.  
  756. CDrawImage::CDrawImage(CBaseWindow *pBaseWindow) :
  757.     m_pBaseWindow(pBaseWindow),
  758.     m_hdc(NULL),
  759.     m_MemoryDC(NULL),
  760.     m_bStretch(FALSE),
  761.     m_pMediaType(NULL),
  762.     m_bUsingImageAllocator(FALSE)
  763. {
  764.     ASSERT(pBaseWindow);
  765.     ResetPaletteVersion();
  766.     SetRectEmpty(&m_TargetRect);
  767.     SetRectEmpty(&m_SourceRect);
  768.  
  769.     m_perfidRenderTime = MSR_REGISTER("Single Blt time");
  770. }
  771.  
  772.  
  773. // Overlay the image time stamps on the picture. Access to this method is
  774. // serialised by the caller. We display the sample start and end times on
  775. // top of the video using TextOut on the device context we are handed. If
  776. // there isn't enough room in the window for the times we don't show them
  777.  
  778. void CDrawImage::DisplaySampleTimes(IMediaSample *pSample)
  779. {
  780.     TCHAR szTimes[TIMELENGTH];      // Time stamp strings
  781.     ASSERT(pSample);                // Quick sanity check
  782.     RECT ClientRect;                // Client window size
  783.     SIZE Size;                      // Size of text output
  784.  
  785.     // Get the time stamps and window size
  786.  
  787.     pSample->GetTime((REFERENCE_TIME*)&m_StartSample, (REFERENCE_TIME*)&m_EndSample);
  788.     HWND hwnd = m_pBaseWindow->GetWindowHWND();
  789.     EXECUTE_ASSERT(GetClientRect(hwnd,&ClientRect));
  790.  
  791.     // Format the sample time stamps
  792.  
  793.     wsprintf(szTimes,TEXT("%08d : %08d"),
  794.              m_StartSample.Millisecs(),
  795.              m_EndSample.Millisecs());
  796.  
  797.     ASSERT(lstrlen(szTimes) < TIMELENGTH);
  798.  
  799.     // Put the times in the middle at the bottom of the window
  800.  
  801.     GetTextExtentPoint32(m_hdc,szTimes,lstrlen(szTimes),&Size);
  802.     INT XPos = ((ClientRect.right - ClientRect.left) - Size.cx) / 2;
  803.     INT YPos = ((ClientRect.bottom - ClientRect.top) - Size.cy) * 4 / 5;
  804.  
  805.     // Check the window is big enough to have sample times displayed
  806.  
  807.     if ((XPos > 0) && (YPos > 0)) {
  808.         TextOut(m_hdc,XPos,YPos,szTimes,lstrlen(szTimes));
  809.     }
  810. }
  811.  
  812.  
  813. // This is called when the drawing code sees that the image has a down level
  814. // palette cookie. We simply call the SetDIBColorTable Windows API with the
  815. // palette that is found after the BITMAPINFOHEADER - we return no errors
  816.  
  817. void CDrawImage::UpdateColourTable(HDC hdc,BITMAPINFOHEADER *pbmi)
  818. {
  819.     ASSERT(pbmi->biClrUsed);
  820.     RGBQUAD *pColourTable = (RGBQUAD *)(pbmi+1);
  821.  
  822.     // Set the new palette in the device context
  823.  
  824.     UINT uiReturn = SetDIBColorTable(hdc,(UINT) 0,
  825.                                      pbmi->biClrUsed,
  826.                                      pColourTable);
  827.  
  828.     // Should always succeed but check in debug builds
  829.     ASSERT(uiReturn == pbmi->biClrUsed);
  830. }
  831.  
  832.  
  833. // No source rectangle scaling is done by the base class
  834.  
  835. RECT CDrawImage::ScaleSourceRect(const RECT *pSource)
  836. {
  837.     ASSERT(pSource);
  838.     return *pSource;
  839. }
  840.  
  841.  
  842. // This is called when the funky output pin uses our allocator. The samples we
  843. // allocate are special because the memory is shared between us and GDI thus
  844. // removing one copy when we ask for the image to be rendered. The source type
  845. // information is in the main renderer m_mtIn field which is initialised when
  846. // the media type is agreed in SetMediaType, the media type may be changed on
  847. // the fly if, for example, the source filter needs to change the palette
  848.  
  849. void CDrawImage::FastRender(IMediaSample *pMediaSample)
  850. {
  851.     BITMAPINFOHEADER *pbmi;     // Image format data
  852.     DIBDATA *pDibData;          // Stores DIB information
  853.     BYTE *pImage;               // Pointer to image data
  854.     HBITMAP hOldBitmap;         // Store the old bitmap
  855.     CImageSample *pSample;      // Pointer to C++ object
  856.  
  857.     ASSERT(m_pMediaType);
  858.  
  859.     // From the untyped source format block get the VIDEOINFO and subsequently
  860.     // the BITMAPINFOHEADER structure. We can cast the IMediaSample interface
  861.     // to a CImageSample object so we can retrieve it's DIBSECTION details
  862.  
  863.     pbmi = HEADER(m_pMediaType->Format());
  864.     pSample = (CImageSample *) pMediaSample;
  865.     pDibData = pSample->GetDIBData();
  866.     hOldBitmap = (HBITMAP) SelectObject(m_MemoryDC,pDibData->hBitmap);
  867.  
  868.     // Get a pointer to the real image data
  869.  
  870.     HRESULT hr = pMediaSample->GetPointer(&pImage);
  871.     if (FAILED(hr)) {
  872.         return;
  873.     }
  874.  
  875.     // Do we need to update the colour table, we increment our palette cookie
  876.     // each time we get a dynamic format change. The sample palette cookie is
  877.     // stored in the DIBDATA structure so we try to keep the fields in sync
  878.     // By the time we get to draw the images the format change will be done
  879.     // so all we do is ask the renderer for what it's palette version is
  880.  
  881.     if (pDibData->PaletteVersion < GetPaletteVersion()) {
  882.         ASSERT(pbmi->biBitCount <= iPALETTE);
  883.         UpdateColourTable(m_MemoryDC,pbmi);
  884.         pDibData->PaletteVersion = GetPaletteVersion();
  885.     }
  886.  
  887.     // This allows derived classes to change the source rectangle that we do
  888.     // the drawing with. For example a renderer may ask a codec to stretch
  889.     // the video from 320x240 to 640x480, in which case the source we see in
  890.     // here will still be 320x240, although the source we want to draw with
  891.     // should be scaled up to 640x480. The base class implementation of this
  892.     // method does nothing but return the same rectangle as we are passed in
  893.  
  894.     RECT SourceRect = ScaleSourceRect(&m_SourceRect);
  895.  
  896.     // Is the window the same size as the video
  897.  
  898.     if (m_bStretch == FALSE) {
  899.  
  900.         // Put the image straight into the window
  901.  
  902.         BitBlt(
  903.             (HDC) m_hdc,                            // Target device HDC
  904.             m_TargetRect.left,                      // X sink position
  905.             m_TargetRect.top,                       // Y sink position
  906.             m_TargetRect.right - m_TargetRect.left, // Destination width
  907.             m_TargetRect.bottom - m_TargetRect.top, // Destination height
  908.             m_MemoryDC,                             // Source device context
  909.             SourceRect.left,                        // X source position
  910.             SourceRect.top,                         // Y source position
  911.             SRCCOPY);                               // Simple copy
  912.  
  913.     } else {
  914.  
  915.         // Stretch the image when copying to the window
  916.  
  917.         StretchBlt(
  918.             (HDC) m_hdc,                            // Target device HDC
  919.             m_TargetRect.left,                      // X sink position
  920.             m_TargetRect.top,                       // Y sink position
  921.             m_TargetRect.right - m_TargetRect.left, // Destination width
  922.             m_TargetRect.bottom - m_TargetRect.top, // Destination height
  923.             m_MemoryDC,                             // Source device HDC
  924.             SourceRect.left,                        // X source position
  925.             SourceRect.top,                         // Y source position
  926.             SourceRect.right - SourceRect.left,     // Source width
  927.             SourceRect.bottom - SourceRect.top,     // Source height
  928.             SRCCOPY);                               // Simple copy
  929.     }
  930.  
  931.     // This displays the sample times over the top of the image. This used to
  932.     // draw the times into the offscreen device context however that actually
  933.     // writes the text into the image data buffer which may not be writable
  934.  
  935.     #ifdef DEBUG
  936.     DisplaySampleTimes(pMediaSample);
  937.     #endif
  938.  
  939.     // Put the old bitmap back into the device context so we don't leak
  940.     SelectObject(m_MemoryDC,hOldBitmap);
  941. }
  942.  
  943.  
  944. // This is called when there is a sample ready to be drawn, unfortunately the
  945. // output pin was being rotten and didn't choose our super excellent shared
  946. // memory DIB allocator so we have to do this slow render using boring old GDI
  947. // SetDIBitsToDevice and StretchDIBits. The down side of using these GDI
  948. // functions is that the image data has to be copied across from our address
  949. // space into theirs before going to the screen (although in reality the cost
  950. // is small because all they do is to map the buffer into their address space)
  951.  
  952. void CDrawImage::SlowRender(IMediaSample *pMediaSample)
  953. {
  954.     // Get the BITMAPINFOHEADER for the connection
  955.  
  956.     ASSERT(m_pMediaType);
  957.     BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format());
  958.     BYTE *pImage;
  959.  
  960.     // Get the image data buffer
  961.  
  962.     HRESULT hr = pMediaSample->GetPointer(&pImage);
  963.     if (FAILED(hr)) {
  964.         return;
  965.     }
  966.  
  967.     // This allows derived classes to change the source rectangle that we do
  968.     // the drawing with. For example a renderer may ask a codec to stretch
  969.     // the video from 320x240 to 640x480, in which case the source we see in
  970.     // here will still be 320x240, although the source we want to draw with
  971.     // should be scaled up to 640x480. The base class implementation of this
  972.     // method does nothing but return the same rectangle as we are passed in
  973.  
  974.     RECT SourceRect = ScaleSourceRect(&m_SourceRect);
  975.  
  976.     // Is the window the same size as the video
  977.  
  978.     if (m_bStretch == FALSE) {
  979.  
  980.         // Put the image straight into the window
  981.  
  982.         SetDIBitsToDevice(
  983.             (HDC) m_hdc,                            // Target device HDC
  984.             m_TargetRect.left,                      // X sink position
  985.             m_TargetRect.top,                       // Y sink position
  986.             m_TargetRect.right - m_TargetRect.left, // Destination width
  987.             m_TargetRect.bottom - m_TargetRect.top, // Destination height
  988.             SourceRect.left,                        // X source position
  989.             SourceRect.top,                         // Y source position
  990.             (UINT) 0,                               // Start scan line
  991.             pbmi->biHeight,                         // Scan lines present
  992.             pImage,                                 // Image data
  993.             (BITMAPINFO *) pbmi,                    // DIB header
  994.             DIB_RGB_COLORS);                        // Type of palette
  995.  
  996.     } else {
  997.  
  998.         // Stretch the image when copying to the window
  999.  
  1000.         StretchDIBits(
  1001.             (HDC) m_hdc,                            // Target device HDC
  1002.             m_TargetRect.left,                      // X sink position
  1003.             m_TargetRect.top,                       // Y sink position
  1004.             m_TargetRect.right - m_TargetRect.left, // Destination width
  1005.             m_TargetRect.bottom - m_TargetRect.top, // Destination height
  1006.             SourceRect.left,                        // X source position
  1007.             SourceRect.top,                         // Y source position
  1008.             SourceRect.right - SourceRect.left,     // Source width
  1009.             SourceRect.bottom - SourceRect.top,     // Source height
  1010.             pImage,                                 // Image data
  1011.             (BITMAPINFO *) pbmi,                    // DIB header
  1012.             DIB_RGB_COLORS,                         // Type of palette
  1013.             SRCCOPY);                               // Simple image copy
  1014.     }
  1015.  
  1016.     // This shows the sample reference times over the top of the image which
  1017.     // looks a little flickery. I tried using GdiSetBatchLimit and GdiFlush to
  1018.     // control the screen updates but it doesn't quite work as expected and
  1019.     // only partially reduces the flicker. I also tried using a memory context
  1020.     // and combining the two in that before doing a final BitBlt operation to
  1021.     // the screen, unfortunately this has considerable performance penalties
  1022.     // and also means that this code is not executed when compiled retail
  1023.  
  1024.     #ifdef DEBUG
  1025.     DisplaySampleTimes(pMediaSample);
  1026.     #endif
  1027. }
  1028.  
  1029.  
  1030. // This is called with an IMediaSample interface on the image to be drawn. We
  1031. // decide on the drawing mechanism based on who's allocator we are using. We
  1032. // may be called when the window wants an image painted by WM_PAINT messages
  1033. // We can't realise the palette here because we have the renderer lock, any
  1034. // call to realise may cause an interthread send message to the window thread
  1035. // which may in turn be waiting to get the renderer lock before servicing it
  1036.  
  1037. BOOL CDrawImage::DrawImage(IMediaSample *pMediaSample)
  1038. {
  1039.     ASSERT(m_hdc);
  1040.     ASSERT(m_MemoryDC);
  1041.     NotifyStartDraw();
  1042.  
  1043.     // If the output pin used our allocator then the samples passed are in
  1044.     // fact CVideoSample objects that contain CreateDIBSection data that we
  1045.     // use to do faster image rendering, they may optionally also contain a
  1046.     // DirectDraw surface pointer in which case we do not do the drawing
  1047.  
  1048.     if (m_bUsingImageAllocator == FALSE) {
  1049.         SlowRender(pMediaSample);
  1050.         EXECUTE_ASSERT(GdiFlush());
  1051.         NotifyEndDraw();
  1052.         return TRUE;
  1053.     }
  1054.  
  1055.     // This is a DIBSECTION buffer
  1056.  
  1057.     FastRender(pMediaSample);
  1058.     EXECUTE_ASSERT(GdiFlush());
  1059.     NotifyEndDraw();
  1060.     return TRUE;
  1061. }
  1062.  
  1063.  
  1064. // This is called by the owning window object after it has created the window
  1065. // and it's drawing contexts. We are constructed with the base window we'll
  1066. // be drawing into so when given the notification we retrive the device HDCs
  1067. // to draw with. We cannot call these in our constructor as they are virtual
  1068.  
  1069. void CDrawImage::SetDrawContext()
  1070. {
  1071.     m_MemoryDC = m_pBaseWindow->GetMemoryHDC();
  1072.     m_hdc = m_pBaseWindow->GetWindowHDC();
  1073. }
  1074.  
  1075.  
  1076. // This is called to set the target rectangle in the video window, it will be
  1077. // called whenever a WM_SIZE message is retrieved from the message queue. We
  1078. // simply store the rectangle and use it later when we do the drawing calls
  1079.  
  1080. void CDrawImage::SetTargetRect(RECT *pTargetRect)
  1081. {
  1082.     ASSERT(pTargetRect);
  1083.     m_TargetRect = *pTargetRect;
  1084.     SetStretchMode();
  1085. }
  1086.  
  1087.  
  1088. // Return the current target rectangle
  1089.  
  1090. void CDrawImage::GetTargetRect(RECT *pTargetRect)
  1091. {
  1092.     ASSERT(pTargetRect);
  1093.     *pTargetRect = m_TargetRect;
  1094. }
  1095.  
  1096.  
  1097. // This is called when we want to change the section of the image to draw. We
  1098. // use this information in the drawing operation calls later on. We must also
  1099. // see if the source and destination rectangles have the same dimensions. If
  1100. // not we must stretch during the drawing rather than a direct pixel copy
  1101.  
  1102. void CDrawImage::SetSourceRect(RECT *pSourceRect)
  1103. {
  1104.     ASSERT(pSourceRect);
  1105.     m_SourceRect = *pSourceRect;
  1106.     SetStretchMode();
  1107. }
  1108.  
  1109.  
  1110. // Return the current source rectangle
  1111.  
  1112. void CDrawImage::GetSourceRect(RECT *pSourceRect)
  1113. {
  1114.     ASSERT(pSourceRect);
  1115.     *pSourceRect = m_SourceRect;
  1116. }
  1117.  
  1118.  
  1119. // This is called when either the source or destination rectanges change so we
  1120. // can update the stretch flag. If the rectangles don't match we stretch the
  1121. // video during the drawing otherwise we call the fast pixel copy functions
  1122. // NOTE the source and/or the destination rectangle may be completely empty
  1123.  
  1124. void CDrawImage::SetStretchMode()
  1125. {
  1126.     // Calculate the overall rectangle dimensions
  1127.  
  1128.     LONG SourceWidth = m_SourceRect.right - m_SourceRect.left;
  1129.     LONG SinkWidth = m_TargetRect.right - m_TargetRect.left;
  1130.     LONG SourceHeight = m_SourceRect.bottom - m_SourceRect.top;
  1131.     LONG SinkHeight = m_TargetRect.bottom - m_TargetRect.top;
  1132.  
  1133.     m_bStretch = TRUE;
  1134.     if (SourceWidth == SinkWidth) {
  1135.         if (SourceHeight == SinkHeight) {
  1136.             m_bStretch = FALSE;
  1137.         }
  1138.     }
  1139. }
  1140.  
  1141.  
  1142. // Tell us whose allocator we are using. This should be called with TRUE if
  1143. // the filter agrees to use an allocator based around the CImageAllocator
  1144. // SDK base class - whose image buffers are made through CreateDIBSection.
  1145. // Otherwise this should be called with FALSE and we will draw the images
  1146. // using SetDIBitsToDevice and StretchDIBitsToDevice. None of these calls
  1147. // can handle buffers which have non zero strides (like DirectDraw uses)
  1148.  
  1149. void CDrawImage::NotifyAllocator(BOOL bUsingImageAllocator)
  1150. {
  1151.     m_bUsingImageAllocator = bUsingImageAllocator;
  1152. }
  1153.  
  1154.  
  1155. // Are we using the image DIBSECTION allocator
  1156.  
  1157. BOOL CDrawImage::UsingImageAllocator()
  1158. {
  1159.     return m_bUsingImageAllocator;
  1160. }
  1161.  
  1162.  
  1163. // We need the media type of the connection so that we can get the BITMAPINFO
  1164. // from it. We use that in the calls to draw the image such as StretchDIBits
  1165. // and also when updating the colour table held in shared memory DIBSECTIONs
  1166.  
  1167. void CDrawImage::NotifyMediaType(CMediaType *pMediaType)
  1168. {
  1169.     m_pMediaType = pMediaType;
  1170. }
  1171.  
  1172.  
  1173. // We store in this object a cookie maintaining the current palette version.
  1174. // Each time a palettised format is changed we increment this value so that
  1175. // when we come to draw the images we look at the colour table value they
  1176. // have and if less than the current we know to update it. This version is
  1177. // only needed and indeed used when working with shared memory DIBSECTIONs
  1178.  
  1179. LONG CDrawImage::GetPaletteVersion()
  1180. {
  1181.     return m_PaletteVersion;
  1182. }
  1183.  
  1184.  
  1185. // Resets the current palette version number
  1186.  
  1187. void CDrawImage::ResetPaletteVersion()
  1188. {
  1189.     m_PaletteVersion = PALETTE_VERSION;
  1190. }
  1191.  
  1192.  
  1193. // Increment the current palette version
  1194.  
  1195. void CDrawImage::IncrementPaletteVersion()
  1196. {
  1197.     m_PaletteVersion++;
  1198. }
  1199.  
  1200.  
  1201. // Constructor must initialise the base allocator. Each sample we create has a
  1202. // palette version cookie on board. When the source filter changes the palette
  1203. // during streaming the window object increments an internal cookie counter it
  1204. // keeps as well. When it comes to render the samples it looks at the cookie
  1205. // values and if they don't match then it knows to update the sample's colour
  1206. // table. However we always create samples with a cookie of PALETTE_VERSION
  1207. // If there have been multiple format changes and we disconnect and reconnect
  1208. // thereby causing the samples to be reallocated we will create them with a
  1209. // cookie much lower than the current version, this isn't a problem since it
  1210. // will be seen by the window object and the versions will then be updated
  1211.  
  1212. CImageAllocator::CImageAllocator(CBaseFilter *pFilter,
  1213.                                  TCHAR *pName,
  1214.                                  HRESULT *phr) :
  1215.     CBaseAllocator(pName,NULL,phr),
  1216.     m_pFilter(pFilter)
  1217. {
  1218.     ASSERT(phr);
  1219.     ASSERT(pFilter);
  1220. }
  1221.  
  1222.  
  1223. // Check our DIB buffers have been released
  1224.  
  1225. CImageAllocator::~CImageAllocator()
  1226. {
  1227.     ASSERT(m_bCommitted == FALSE);
  1228. }
  1229.  
  1230.  
  1231. // Called from destructor and also from base class to free resources. We work
  1232. // our way through the list of media samples deleting the DIBSECTION created
  1233. // for each. All samples should be back in our list so there is no chance a
  1234. // filter is still using one to write on the display or hold on a pending list
  1235.  
  1236. void CImageAllocator::Free()
  1237. {
  1238.     ASSERT(m_lAllocated == m_lFree.GetCount());
  1239.     EXECUTE_ASSERT(GdiFlush());
  1240.     CImageSample *pSample;
  1241.     DIBDATA *pDibData;
  1242.  
  1243.     while (m_lFree.GetCount() != 0) {
  1244.         pSample = (CImageSample *) m_lFree.RemoveHead();
  1245.         pDibData = pSample->GetDIBData();
  1246.         EXECUTE_ASSERT(DeleteObject(pDibData->hBitmap));
  1247.         EXECUTE_ASSERT(CloseHandle(pDibData->hMapping));
  1248.         delete pSample;
  1249.     }
  1250.  
  1251.     m_lAllocated = 0;
  1252. }
  1253.  
  1254.  
  1255. // Prepare the allocator by checking all the input parameters
  1256.  
  1257. STDMETHODIMP CImageAllocator::CheckSizes(ALLOCATOR_PROPERTIES *pRequest)
  1258. {
  1259.     // Check we have a valid connection
  1260.  
  1261.     if (m_pMediaType == NULL) {
  1262.         return VFW_E_NOT_CONNECTED;
  1263.     }
  1264.  
  1265.     // NOTE We always create a DIB section with the source format type which
  1266.     // may contain a source palette. When we do the BitBlt drawing operation
  1267.     // the target display device may contain a different palette (we may not
  1268.     // have the focus) in which case GDI will do after the palette mapping
  1269.  
  1270.     VIDEOINFO *pVideoInfo = (VIDEOINFO *) m_pMediaType->Format();
  1271.  
  1272.     // When we call CreateDIBSection it implicitly maps only enough memory
  1273.     // for the image as defined by thee BITMAPINFOHEADER. If the user asks
  1274.     // for an image smaller than this then we reject the call, if they ask
  1275.     // for an image larger than this then we return what they can have
  1276.  
  1277.     if ((DWORD) pRequest->cbBuffer < pVideoInfo->bmiHeader.biSizeImage) {
  1278.         return E_INVALIDARG;
  1279.     }
  1280.  
  1281.     // Reject buffer prefixes
  1282.  
  1283.     if (pRequest->cbPrefix > 0) {
  1284.         return E_INVALIDARG;
  1285.     }
  1286.  
  1287.     pRequest->cbBuffer = pVideoInfo->bmiHeader.biSizeImage;
  1288.     return NOERROR;
  1289. }
  1290.  
  1291.  
  1292. // Agree the number of media sample buffers and their sizes. The base class
  1293. // this allocator is derived from allows samples to be aligned only on byte
  1294. // boundaries NOTE the buffers are not allocated until the Commit call
  1295.  
  1296. STDMETHODIMP CImageAllocator::SetProperties(
  1297.     ALLOCATOR_PROPERTIES * pRequest,
  1298.     ALLOCATOR_PROPERTIES * pActual)
  1299. {
  1300.     ALLOCATOR_PROPERTIES Adjusted = *pRequest;
  1301.  
  1302.     // Check the parameters fit with the current connection
  1303.  
  1304.     HRESULT hr = CheckSizes(&Adjusted);
  1305.     if (FAILED(hr)) {
  1306.         return hr;
  1307.     }
  1308.     return CBaseAllocator::SetProperties(&Adjusted, pActual);
  1309. }
  1310.  
  1311.  
  1312. // Commit the memory by allocating the agreed number of media samples. For
  1313. // each sample we are committed to creating we have a CImageSample object
  1314. // that we use to manage it's resources. This is initialised with a DIBDATA
  1315. // structure that contains amongst other things the GDI DIBSECTION handle
  1316. // We will access the renderer media type during this so we must have locked
  1317. // (to prevent the format changing for example). The class overrides Commit
  1318. // and Decommit to do this locking (base class Commit in turn calls Alloc)
  1319.  
  1320. HRESULT CImageAllocator::Alloc(void)
  1321. {
  1322.     ASSERT(m_pMediaType);
  1323.     CImageSample *pSample;
  1324.     DIBDATA DibData;
  1325.  
  1326.     // Check the base allocator says it's ok to continue
  1327.  
  1328.     HRESULT hr = CBaseAllocator::Alloc();
  1329.     if (FAILED(hr)) {
  1330.         return hr;
  1331.     }
  1332.  
  1333.     // We create a new memory mapped object although we don't map it into our
  1334.     // address space because GDI does that in CreateDIBSection. It is possible
  1335.     // that we run out of resources before creating all the samples in which
  1336.     // case the available sample list is left with those already created
  1337.  
  1338.     ASSERT(m_lAllocated == 0);
  1339.     while (m_lAllocated < m_lCount) {
  1340.  
  1341.         // Create and initialise a shared memory GDI buffer
  1342.  
  1343.         HRESULT hr = CreateDIB(m_lSize,DibData);
  1344.         if (FAILED(hr)) {
  1345.             return hr;
  1346.         }
  1347.  
  1348.         // Create the sample object and pass it the DIBDATA
  1349.  
  1350.         pSample = CreateImageSample(DibData.pBase,m_lSize);
  1351.         if (pSample == NULL) {
  1352.             EXECUTE_ASSERT(DeleteObject(DibData.hBitmap));
  1353.             EXECUTE_ASSERT(CloseHandle(DibData.hMapping));
  1354.             return E_OUTOFMEMORY;
  1355.         }
  1356.  
  1357.         // Add the completed sample to the available list
  1358.  
  1359.         pSample->SetDIBData(&DibData);
  1360.         m_lFree.Add(pSample);
  1361.         m_lAllocated++;
  1362.     }
  1363.     return NOERROR;
  1364. }
  1365.  
  1366.  
  1367. // We have a virtual method that allocates the samples so that a derived class
  1368. // may override it and allocate more specialised sample objects. So long as it
  1369. // derives its samples from CImageSample then all this code will still work ok
  1370.  
  1371. CImageSample *CImageAllocator::CreateImageSample(LPBYTE pData,LONG Length)
  1372. {
  1373.     HRESULT hr = NOERROR;
  1374.     CImageSample *pSample;
  1375.  
  1376.     // Allocate the new sample and check the return codes
  1377.  
  1378.     pSample = new CImageSample((CBaseAllocator *) this,   // Base class
  1379.                                NAME("Video sample"),      // DEBUG name
  1380.                                (HRESULT *) &hr,           // Return code
  1381.                                (LPBYTE) pData,            // DIB address
  1382.                                (LONG) Length);            // Size of DIB
  1383.  
  1384.     if (pSample == NULL || FAILED(hr)) {
  1385.         delete pSample;
  1386.         return NULL;
  1387.     }
  1388.     return pSample;
  1389. }
  1390.  
  1391.  
  1392. // This function allocates a shared memory block for use by the source filter
  1393. // generating DIBs for us to render. The memory block is created in shared
  1394. // memory so that GDI doesn't have to copy the memory when we do a BitBlt
  1395.  
  1396. HRESULT CImageAllocator::CreateDIB(LONG InSize,DIBDATA &DibData)
  1397. {
  1398.     BITMAPINFO *pbmi;       // Format information for pin
  1399.     BYTE *pBase;            // Pointer to the actual image
  1400.     HANDLE hMapping;        // Handle to mapped object
  1401.     HBITMAP hBitmap;        // DIB section bitmap handle
  1402.  
  1403.     // Create a file mapping object and map into our address space
  1404.  
  1405.     hMapping = CreateFileMapping(hMEMORY,         // Use system page file
  1406.                                  NULL,            // No security attributes
  1407.                                  PAGE_READWRITE,  // Full access to memory
  1408.                                  (DWORD) 0,       // Less than 4Gb in size
  1409.                                  InSize,          // Size of buffer
  1410.                                  NULL);           // No name to section
  1411.     if (hMapping == NULL) {
  1412.         DWORD Error = GetLastError();
  1413.         return HRESULT_FROM_WIN32(Error);
  1414.     }
  1415.  
  1416.     // NOTE We always create a DIB section with the source format type which
  1417.     // may contain a source palette. When we do the BitBlt drawing operation
  1418.     // the target display device may contain a different palette (we may not
  1419.     // have the focus) in which case GDI will do after the palette mapping
  1420.  
  1421.     pbmi = (BITMAPINFO *) HEADER(m_pMediaType->Format());
  1422.     if (m_pMediaType == NULL) {
  1423.         DbgBreak("Invalid media type");
  1424.     }
  1425.  
  1426.     hBitmap = CreateDIBSection((HDC) NULL,          // NO device context
  1427.                                pbmi,                // Format information
  1428.                                DIB_RGB_COLORS,      // Use the palette
  1429.                                (VOID **) &pBase,    // Pointer to image data
  1430.                                hMapping,            // Mapped memory handle
  1431.                                (DWORD) 0);          // Offset into memory
  1432.  
  1433.     if (hBitmap == NULL || pBase == NULL) {
  1434.         EXECUTE_ASSERT(CloseHandle(hMapping));
  1435.         DWORD Error = GetLastError();
  1436.         return HRESULT_FROM_WIN32(Error);
  1437.     }
  1438.  
  1439.     // Initialise the DIB information structure
  1440.  
  1441.     DibData.hBitmap = hBitmap;
  1442.     DibData.hMapping = hMapping;
  1443.     DibData.pBase = pBase;
  1444.     DibData.PaletteVersion = PALETTE_VERSION;
  1445.     GetObject(hBitmap,sizeof(DIBSECTION),(VOID *)&DibData.DibSection);
  1446.  
  1447.     return NOERROR;
  1448. }
  1449.  
  1450.  
  1451. // We use the media type during the DIBSECTION creation
  1452.  
  1453. void CImageAllocator::NotifyMediaType(CMediaType *pMediaType)
  1454. {
  1455.     m_pMediaType = pMediaType;
  1456. }
  1457.  
  1458.  
  1459. // Overriden to increment the owning object's reference count
  1460.  
  1461. STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingAddRef()
  1462. {
  1463.     return m_pFilter->AddRef();
  1464. }
  1465.  
  1466.  
  1467. // Overriden to decrement the owning object's reference count
  1468.  
  1469. STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingRelease()
  1470. {
  1471.     return m_pFilter->Release();
  1472. }
  1473.  
  1474.  
  1475. // If you derive a class from CMediaSample that has to transport specialised
  1476. // member variables and entry points then there are three alternate solutions
  1477. // The first is to create a memory buffer larger than actually required by the
  1478. // sample and store your information either at the beginning of it or at the
  1479. // end, the former being moderately safer allowing for misbehaving transform
  1480. // filters. You then adjust the buffer address when you create the base media
  1481. // sample. This has the disadvantage of breaking up the memory allocated to
  1482. // the samples into separate blocks. The second solution is to implement a
  1483. // class derived from CMediaSample and support additional interface(s) that
  1484. // convey your private data. This means defining a custom interface. The final
  1485. // alternative is to create a class that inherits from CMediaSample and adds
  1486. // the private data structures, when you get an IMediaSample in your Receive()
  1487. // call check to see if your allocator is being used, and if it is then cast
  1488. // the IMediaSample into one of your objects. Additional checks can be made
  1489. // to ensure the sample's this pointer is known to be one of your own objects
  1490.  
  1491. CImageSample::CImageSample(CBaseAllocator *pAllocator,
  1492.                            TCHAR *pName,
  1493.                            HRESULT *phr,
  1494.                            LPBYTE pBuffer,
  1495.                            LONG length) :
  1496.     CMediaSample(pName,pAllocator,phr,pBuffer,length),
  1497.     m_bInit(FALSE)
  1498. {
  1499.     ASSERT(pAllocator);
  1500.     ASSERT(pBuffer);
  1501. }
  1502.  
  1503.  
  1504. // Set the shared memory DIB information
  1505.  
  1506. void CImageSample::SetDIBData(DIBDATA *pDibData)
  1507. {
  1508.     ASSERT(pDibData);
  1509.     m_DibData = *pDibData;
  1510.     m_bInit = TRUE;
  1511. }
  1512.  
  1513.  
  1514. // Retrieve the shared memory DIB data
  1515.  
  1516. DIBDATA *CImageSample::GetDIBData()
  1517. {
  1518.     ASSERT(m_bInit == TRUE);
  1519.     return &m_DibData;
  1520. }
  1521.  
  1522.  
  1523. // This class handles the creation of a palette. It is fairly specialist and
  1524. // is intended to simplify palette management for video renderer filters. It
  1525. // is for this reason that the constructor requires three other objects with
  1526. // which it interacts, namely a base media filter, a base window and a base
  1527. // drawing object although the base window or the draw object may be NULL to
  1528. // ignore that part of us. We try not to create and install palettes unless
  1529. // absolutely necessary as they typically require WM_PALETTECHANGED messages
  1530. // to be sent to every window thread in the system which is very expensive
  1531.  
  1532. CImagePalette::CImagePalette(CBaseFilter *pBaseFilter,
  1533.                              CBaseWindow *pBaseWindow,
  1534.                              CDrawImage *pDrawImage) :
  1535.     m_pBaseWindow(pBaseWindow),
  1536.     m_pFilter(pBaseFilter),
  1537.     m_pDrawImage(pDrawImage),
  1538.     m_hPalette(NULL)
  1539. {
  1540.     ASSERT(m_pFilter);
  1541. }
  1542.  
  1543.  
  1544. // Destructor
  1545.  
  1546. CImagePalette::~CImagePalette()
  1547. {
  1548.     ASSERT(m_hPalette == NULL);
  1549. }
  1550.  
  1551.  
  1552. // We allow dynamic format changes of the palette but rather than change the
  1553. // palette every time we call this to work out whether an update is required.
  1554. // If the original type didn't use a palette and the new one does (or vica
  1555. // versa) then we return TRUE. If neither formats use a palette we'll return
  1556. // FALSE. If both formats use a palette we compare their colours and return
  1557. // FALSE if they match. This therefore short circuits palette creation unless
  1558. // absolutely necessary since installing palettes is an expensive operation
  1559.  
  1560. BOOL CImagePalette::ShouldUpdate(const VIDEOINFO *pNewInfo,
  1561.                                  const VIDEOINFO *pOldInfo)
  1562. {
  1563.     // We may not have a current format yet
  1564.  
  1565.     if (pOldInfo == NULL) {
  1566.         return TRUE;
  1567.     }
  1568.  
  1569.     // Do both formats not require a palette
  1570.  
  1571.     if (ContainsPalette(pNewInfo) == FALSE) {
  1572.         if (ContainsPalette(pOldInfo) == FALSE) {
  1573.             return FALSE;
  1574.         }
  1575.     }
  1576.  
  1577.     // Compare the colours to see if they match
  1578.  
  1579.     DWORD VideoEntries = pNewInfo->bmiHeader.biClrUsed;
  1580.     if (ContainsPalette(pNewInfo) == TRUE)
  1581.         if (ContainsPalette(pOldInfo) == TRUE)
  1582.             if (pOldInfo->bmiHeader.biClrUsed == VideoEntries)
  1583.                 if (pOldInfo->bmiHeader.biClrUsed > 0)
  1584.                     if (memcmp((PVOID) GetBitmapPalette(pNewInfo),
  1585.                                (PVOID) GetBitmapPalette(pOldInfo),
  1586.                                VideoEntries * sizeof(RGBQUAD)) == 0) {
  1587.  
  1588.                         return FALSE;
  1589.                     }
  1590.     return TRUE;
  1591. }
  1592.  
  1593.  
  1594. // This is normally called when the input pin type is set to install a palette
  1595. // We will typically be called from two different places. The first is when we
  1596. // have negotiated a palettised media type after connection, the other is when
  1597. // we receive a new type during processing with an updated palette in which
  1598. // case we must remove and release the resources held by the current palette
  1599.  
  1600. HRESULT CImagePalette::PreparePalette(const CMediaType *pmtNew,
  1601.                                       const CMediaType *pmtOld)
  1602. {
  1603.     const VIDEOINFO *pNewInfo = (VIDEOINFO *) pmtNew->Format();
  1604.     const VIDEOINFO *pOldInfo = (VIDEOINFO *) pmtOld->Format();
  1605.     ASSERT(pNewInfo);
  1606.  
  1607.     // This is an performance optimisation, when we get a media type we check
  1608.     // to see if the format requires a palette change. If either we need one
  1609.     // when previously we didn't or vica versa then this returns TRUE, if we
  1610.     // previously needed a palette and we do now it compares their colours
  1611.  
  1612.     if (ShouldUpdate(pNewInfo,pOldInfo) == FALSE) {
  1613.         NOTE("No update needed");
  1614.         return S_FALSE;
  1615.     }
  1616.  
  1617.     // We must notify the filter graph that the application may have changed
  1618.     // the palette although in practice we don't bother checking to see if it
  1619.     // is really different. If it tries to get the palette either the window
  1620.     // or renderer lock will ensure it doesn't get in until we are finished
  1621.  
  1622.     RemovePalette();
  1623.     m_pFilter->NotifyEvent(EC_PALETTE_CHANGED,0,0);
  1624.  
  1625.     // Do we need a palette for the new format
  1626.  
  1627.     if (ContainsPalette(pNewInfo) == FALSE) {
  1628.         NOTE("New has no palette");
  1629.         return S_FALSE;
  1630.     }
  1631.  
  1632.     // If we're changing the palette on the fly then we increment our palette
  1633.     // cookie which is compared against the cookie also stored in all of our
  1634.     // DIBSECTION media samples. If they don't match when we come to draw it
  1635.     // then we know the sample is out of date and we'll update it's palette
  1636.  
  1637.     NOTE("Making new colour palette");
  1638.     m_hPalette = MakePalette(pNewInfo);
  1639.     ASSERT(m_hPalette != NULL);
  1640.  
  1641.     // The window in which the new palette is to be realised may be a NULL
  1642.     // pointer to signal that no window is in use, if so we don't call it
  1643.     // Some filters just want to use this object to create/manage palettes
  1644.  
  1645.     if (m_pBaseWindow) m_pBaseWindow->SetPalette(m_hPalette);
  1646.  
  1647.     // This is the only time where we need access to the draw object to say
  1648.     // to it that a new palette will be arriving on a sample real soon. The
  1649.     // constructor may take a NULL pointer in which case we don't call this
  1650.  
  1651.     if (m_pDrawImage) m_pDrawImage->IncrementPaletteVersion();
  1652.     return NOERROR;
  1653. }
  1654.  
  1655.  
  1656. // Helper function to copy a palette out of any kind of VIDEOINFO (ie it may
  1657. // be YUV or true colour) into a palettised VIDEOINFO. We use this changing
  1658. // palettes on DirectDraw samples as a source filter can attach a palette to
  1659. // any buffer (eg YUV) and hand it back. We make a new palette out of that
  1660. // format and then copy the palette colours into the current connection type
  1661.  
  1662. HRESULT CImagePalette::CopyPalette(const CMediaType *pSrc,CMediaType *pDest)
  1663. {
  1664.     // Reset the destination palette before starting
  1665.  
  1666.     VIDEOINFO *pDestInfo = (VIDEOINFO *) pDest->Format();
  1667.     pDestInfo->bmiHeader.biClrUsed = 0;
  1668.     pDestInfo->bmiHeader.biClrImportant = 0;
  1669.  
  1670.     // Does the destination have a palette
  1671.  
  1672.     if (PALETTISED(pDestInfo) == FALSE) {
  1673.         NOTE("No destination palette");
  1674.         return S_FALSE;
  1675.     }
  1676.  
  1677.     // Does the source contain a palette
  1678.  
  1679.     const VIDEOINFO *pSrcInfo = (VIDEOINFO *) pSrc->Format();
  1680.     if (ContainsPalette(pSrcInfo) == FALSE) {
  1681.         NOTE("No source palette");
  1682.         return S_FALSE;
  1683.     }
  1684.  
  1685.     // The number of colours may be zero filled
  1686.  
  1687.     DWORD PaletteEntries = pSrcInfo->bmiHeader.biClrUsed;
  1688.     if (PaletteEntries == 0) {
  1689.         DWORD Maximum  = (1 << pSrcInfo->bmiHeader.biBitCount);
  1690.         NOTE1("Setting maximum colours (%d)",Maximum);
  1691.         PaletteEntries = Maximum;
  1692.     }
  1693.  
  1694.     // Make sure the destination has enough room for the palette
  1695.  
  1696.     ASSERT(pSrcInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);
  1697.     ASSERT(pSrcInfo->bmiHeader.biClrImportant <= PaletteEntries);
  1698.     ASSERT(pDestInfo->bmiColors == GetBitmapPalette(pDestInfo));
  1699.     pDestInfo->bmiHeader.biClrUsed = PaletteEntries;
  1700.     pDestInfo->bmiHeader.biClrImportant = pSrcInfo->bmiHeader.biClrImportant;
  1701.     ULONG BitmapSize = GetBitmapFormatSize(HEADER(pSrcInfo));
  1702.  
  1703.     if (pDest->FormatLength() < BitmapSize) {
  1704.         NOTE("Reallocating destination");
  1705.         pDest->ReallocFormatBuffer(BitmapSize);
  1706.     }
  1707.  
  1708.     // Now copy the palette colours across
  1709.  
  1710.     CopyMemory((PVOID) pDestInfo->bmiColors,
  1711.                (PVOID) GetBitmapPalette(pSrcInfo),
  1712.                PaletteEntries * sizeof(RGBQUAD));
  1713.  
  1714.     return NOERROR;
  1715. }
  1716.  
  1717.  
  1718. // This is normally called when the palette is changed (typically during a
  1719. // dynamic format change) to remove any palette we previously installed. We
  1720. // replace it (if necessary) in the video window with a standard VGA palette
  1721. // that should always be available even if this is a true colour display
  1722.  
  1723. HRESULT CImagePalette::RemovePalette()
  1724. {
  1725.     // Do we have a palette to remove
  1726.  
  1727.     if (m_hPalette == NULL) {
  1728.         return NOERROR;
  1729.     }
  1730.  
  1731.     // Get a standard VGA colour palette
  1732.  
  1733.     HPALETTE hPalette = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
  1734.     ASSERT(hPalette);
  1735.  
  1736.     // Install the standard palette and delete ours. As in the previous method
  1737.     // we may not have been given a window in the constructor to use and if we
  1738.     // didn't then don't try to install the stock palette in it. This is used
  1739.     // by filters that have to create palettes but who do not draw using GDI
  1740.  
  1741.     if (m_pBaseWindow) {
  1742.         m_pBaseWindow->SetPalette(hPalette);
  1743.     }
  1744.  
  1745.     EXECUTE_ASSERT(DeleteObject(m_hPalette));
  1746.     m_hPalette = NULL;
  1747.     return NOERROR;
  1748. }
  1749.  
  1750.  
  1751. // Called to create a palette for the object, the data structure used by GDI
  1752. // to describe a palette is a LOGPALETTE, this includes a variable number of
  1753. // PALETTEENTRY fields which are the colours, we have to convert the RGBQUAD
  1754. // colour fields we are handed in a BITMAPINFO from the media type into these
  1755. // This handles extraction of palettes from true colour and YUV media formats
  1756.  
  1757. HPALETTE CImagePalette::MakePalette(const VIDEOINFO *pVideoInfo)
  1758. {
  1759.     ASSERT(ContainsPalette(pVideoInfo) == TRUE);
  1760.     ASSERT(pVideoInfo->bmiHeader.biClrUsed >= 0);
  1761.     ASSERT(pVideoInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);
  1762.     BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo);
  1763.  
  1764.     const RGBQUAD *pColours;            // Pointer to the palette
  1765.     LOGPALETTE *lp;                     // Used to create a palette
  1766.     HPALETTE hPalette;                  // Logical palette object
  1767.  
  1768.     lp = (LOGPALETTE *) new BYTE[sizeof(LOGPALETTE) + SIZE_PALETTE];
  1769.     if (lp == NULL) {
  1770.         return NULL;
  1771.     }
  1772.  
  1773.     // Unfortunately for some hare brained reason a GDI palette entry (a
  1774.     // PALETTEENTRY structure) is different to a palette entry from a DIB
  1775.     // format (a RGBQUAD structure) so we have to do the field conversion
  1776.     // The VIDEOINFO containing the palette may be a true colour type so
  1777.     // we use GetBitmapPalette to skip over any bit fields if they exist
  1778.  
  1779.     lp->palVersion = PALVERSION;
  1780.     lp->palNumEntries = (USHORT) pHeader->biClrUsed;
  1781.     if (lp->palNumEntries == 0) lp->palNumEntries = (1 << pHeader->biBitCount);
  1782.     pColours = GetBitmapPalette(pVideoInfo);
  1783.  
  1784.     for (DWORD dwCount = 0;dwCount < lp->palNumEntries;dwCount++) {
  1785.         lp->palPalEntry[dwCount].peRed = pColours[dwCount].rgbRed;
  1786.         lp->palPalEntry[dwCount].peGreen = pColours[dwCount].rgbGreen;
  1787.         lp->palPalEntry[dwCount].peBlue = pColours[dwCount].rgbBlue;
  1788.         lp->palPalEntry[dwCount].peFlags = 0;
  1789.     }
  1790.  
  1791.     MakeIdentityPalette(lp->palPalEntry,lp->palNumEntries);
  1792.  
  1793.     // Create a logical palette
  1794.  
  1795.     hPalette = CreatePalette(lp);
  1796.     ASSERT(hPalette != NULL);
  1797.     delete[] lp;
  1798.     return hPalette;
  1799. }
  1800.  
  1801.  
  1802. // GDI does a fair job of compressing the palette entries you give it, so for
  1803. // example if you have five entries with an RGB colour (0,0,0) it will remove
  1804. // all but one of them. When you subsequently draw an image it will map from
  1805. // your logical palette to the compressed device palette. This function looks
  1806. // to see if it is trying to be an identity palette and if so sets the flags
  1807. // field in the PALETTEENTRYs so they remain expanded to boost performance
  1808.  
  1809. HRESULT CImagePalette::MakeIdentityPalette(PALETTEENTRY *pEntry,INT iColours)
  1810. {
  1811.     PALETTEENTRY SystemEntries[10];           // System palette entries
  1812.     BOOL bIdentityPalette = TRUE;             // Is an identity palette
  1813.     ASSERT(iColours <= iPALETTE_COLORS);      // Should have a palette
  1814.  
  1815.     // Does this have the full colour range
  1816.  
  1817.     if (iColours < iPALETTE_COLORS) {
  1818.         return S_FALSE;
  1819.     }
  1820.  
  1821.     // Apparently some displays have odd numbers of system colours
  1822.  
  1823.     HDC hdc = GetDC(NULL);
  1824.     INT Reserved = GetDeviceCaps(hdc,NUMRESERVED);
  1825.     if (Reserved != 20) {
  1826.         ReleaseDC(NULL,hdc);
  1827.         return S_FALSE;
  1828.     }
  1829.  
  1830.     // Compare our palette against the first ten system entries. The reason I
  1831.     // don't do a memory compare between our two arrays of colours is because
  1832.     // I am not sure what will be in the flags fields for the system entries
  1833.  
  1834.     UINT Result = GetSystemPaletteEntries(hdc,0,10,SystemEntries);
  1835.     for (UINT Count = 0;Count < Result;Count++) {
  1836.         if (SystemEntries[Count].peRed != pEntry[Count].peRed ||
  1837.             SystemEntries[Count].peGreen != pEntry[Count].peGreen ||
  1838.             SystemEntries[Count].peBlue != pEntry[Count].peBlue) {
  1839.  
  1840.                 bIdentityPalette = FALSE;
  1841.         }
  1842.     }
  1843.  
  1844.     // And likewise compare against the last ten entries
  1845.  
  1846.     Result = GetSystemPaletteEntries(hdc,246,10,SystemEntries);
  1847.     for (Count = 0;Count < Result;Count++) {
  1848.         if (SystemEntries[Count].peRed != pEntry[246 + Count].peRed ||
  1849.             SystemEntries[Count].peGreen != pEntry[246 + Count].peGreen ||
  1850.             SystemEntries[Count].peBlue != pEntry[246 + Count].peBlue) {
  1851.  
  1852.                 bIdentityPalette = FALSE;
  1853.         }
  1854.     }
  1855.  
  1856.     // If not an identity palette then return S_FALSE
  1857.  
  1858.     ReleaseDC(NULL,hdc);
  1859.     if (bIdentityPalette == FALSE) {
  1860.         return S_FALSE;
  1861.     }
  1862.  
  1863.     // Set the non VGA entries so that GDI doesn't map them
  1864.  
  1865.     for (Count = 10;Count < 246;Count++) {
  1866.         pEntry[Count].peFlags = PC_NOCOLLAPSE;
  1867.     }
  1868.     return NOERROR;
  1869. }
  1870.  
  1871.  
  1872. // Constructor initialises the VIDEOINFO we keep storing the current display
  1873. // format. The format can be changed at any time, to reset the format held
  1874. // by us call the RefreshDisplayType directly (it's a public method). Since
  1875. // more than one thread will typically call us (ie window threads resetting
  1876. // the type and source threads in the type checking methods) we have a lock
  1877.  
  1878. CImageDisplay::CImageDisplay()
  1879. {
  1880.     RefreshDisplayType();
  1881. }
  1882.  
  1883.  
  1884. // Destructor (virtual placeholder)
  1885.  
  1886. CImageDisplay::~CImageDisplay()
  1887. {
  1888. }
  1889.  
  1890.  
  1891. // This initialises the format we hold which contains the display device type
  1892. // We do a conversion on the display device type in here so that when we start
  1893. // type checking input formats we can assume that certain fields have been set
  1894. // correctly, an example is when we make the 16 bit mask fields explicit. This
  1895. // is normally called when we receive WM_DEVMODECHANGED device change messages
  1896.  
  1897. HRESULT CImageDisplay::RefreshDisplayType()
  1898. {
  1899.     CAutoLock cDisplayLock(this);
  1900.  
  1901.     // Set the preferred format type
  1902.  
  1903.     ZeroMemory((PVOID)&m_Display,sizeof(VIDEOINFO));
  1904.     m_Display.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  1905.     m_Display.bmiHeader.biBitCount = FALSE;
  1906.  
  1907.     // Get the bit depth of a device compatible bitmap
  1908.  
  1909.     HDC hdcDisplay = GetDC(NULL);
  1910.     HBITMAP hbm = CreateCompatibleBitmap(hdcDisplay,1,1);
  1911.     GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);
  1912.  
  1913.     // This call will get the colour table or the proper bitfields
  1914.  
  1915.     GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);
  1916.     DeleteObject(hbm);
  1917.     ReleaseDC(NULL,hdcDisplay);
  1918.  
  1919.     // Complete the display type initialisation
  1920.  
  1921.     ASSERT(CheckHeaderValidity(&m_Display));
  1922.     UpdateFormat(&m_Display);
  1923.     return NOERROR;
  1924. }
  1925.  
  1926.  
  1927. // We assume throughout this code that any bitfields masks are allowed no
  1928. // more than eight bits to store a colour component. This checks that the
  1929. // bit count assumption is enforced and also makes sure that all the bits
  1930. // set are contiguous. We return a boolean TRUE if the field checks out ok
  1931.  
  1932. BOOL CImageDisplay::CheckBitFields(const VIDEOINFO *pInput)
  1933. {
  1934.     DWORD *pBitFields = (DWORD *) pInput->dwBitMasks;
  1935.  
  1936.     for (INT iColour = iRED;iColour <= iBLUE;iColour++) {
  1937.  
  1938.         // First of all work out how many bits are set
  1939.  
  1940.         DWORD SetBits = CountSetBits(pBitFields[iColour]);
  1941.         if (SetBits > iMAXBITS || SetBits == 0) {
  1942.             NOTE1("Bit fields for component %d invalid",iColour);
  1943.             return FALSE;
  1944.         }
  1945.  
  1946.         // Next work out the number of zero bits prefix
  1947.         DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);
  1948.  
  1949.         // This is going to see if all the bits set are contiguous (as they
  1950.         // should be). We know how much to shift them right by from the
  1951.         // count of prefix bits. The number of bits set defines a mask, we
  1952.         // invert this (ones complement) and AND it with the shifted bit
  1953.         // fields. If the result is NON zero then there are bit(s) sticking
  1954.         // out the left hand end which means they are not contiguous
  1955.  
  1956.         DWORD TestField = pBitFields[iColour] >> PrefixBits;
  1957.         DWORD Mask = ULONG_MAX << SetBits;
  1958.         if (TestField & Mask) {
  1959.             NOTE1("Bit fields for component %d not contiguous",iColour);
  1960.             return FALSE;
  1961.         }
  1962.     }
  1963.     return TRUE;
  1964. }
  1965.  
  1966.  
  1967. // This counts the number of bits set in the input field
  1968.  
  1969. DWORD CImageDisplay::CountSetBits(const DWORD Field)
  1970. {
  1971.     // This is a relatively well known bit counting algorithm
  1972.  
  1973.     DWORD Count = 0;
  1974.     DWORD init = Field;
  1975.  
  1976.     // Until the input is exhausted, count the number of bits
  1977.  
  1978.     while (init) {
  1979.         init = init & (init - 1);  // Turn off the bottommost bit
  1980.         Count++;
  1981.     }
  1982.     return Count;
  1983. }
  1984.  
  1985.  
  1986. // This counts the number of zero bits upto the first one set NOTE the input
  1987. // field should have been previously checked to ensure there is at least one
  1988. // set although if we don't find one set we return the impossible value 32
  1989.  
  1990. DWORD CImageDisplay::CountPrefixBits(const DWORD Field)
  1991. {
  1992.     DWORD Mask = 1;
  1993.     DWORD Count = 0;
  1994.  
  1995.     while (TRUE) {
  1996.         if (Field & Mask) {
  1997.             return Count;
  1998.         }
  1999.         Count++;
  2000.  
  2001.         ASSERT(Mask != 0x80000000);
  2002.         if (Mask == 0x80000000) {
  2003.             return Count;
  2004.         }
  2005.         Mask <<= 1;
  2006.     }
  2007. }
  2008.  
  2009.  
  2010. // This is called to check the BITMAPINFOHEADER for the input type. There are
  2011. // many implicit dependancies between the fields in a header structure which
  2012. // if we validate now make for easier manipulation in subsequent handling. We
  2013. // also check that the BITMAPINFOHEADER matches it's specification such that
  2014. // fields likes the number of planes is one, that it's structure size is set
  2015. // correctly and that the bitmap dimensions have not been set as negative
  2016.  
  2017. BOOL CImageDisplay::CheckHeaderValidity(const VIDEOINFO *pInput)
  2018. {
  2019.     CAutoLock cDisplayLock(this);
  2020.  
  2021.     // Check the bitmap dimensions are not negative
  2022.  
  2023.     if (pInput->bmiHeader.biWidth <= 0 || pInput->bmiHeader.biHeight <= 0) {
  2024.         NOTE("Invalid bitmap dimensions");
  2025.         return FALSE;
  2026.     }
  2027.  
  2028.     // Check the compression is either BI_RGB or BI_BITFIELDS
  2029.  
  2030.     if (pInput->bmiHeader.biCompression != BI_RGB) {
  2031.         if (pInput->bmiHeader.biCompression != BI_BITFIELDS) {
  2032.             NOTE("Invalid compression format");
  2033.             return FALSE;
  2034.         }
  2035.     }
  2036.  
  2037.     // If BI_BITFIELDS compression format check the colour depth
  2038.  
  2039.     if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {
  2040.         if (pInput->bmiHeader.biBitCount != 16) {
  2041.             if (pInput->bmiHeader.biBitCount != 32) {
  2042.                 NOTE("BI_BITFIELDS not 16/32 bit depth");
  2043.                 return FALSE;
  2044.             }
  2045.         }
  2046.     }
  2047.  
  2048.     // Check the assumptions about the layout of the bit fields
  2049.  
  2050.     if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {
  2051.         if (CheckBitFields(pInput) == FALSE) {
  2052.             NOTE("Bit fields are not valid");
  2053.             return FALSE;
  2054.         }
  2055.     }
  2056.  
  2057.     // Are the number of planes equal to one
  2058.  
  2059.     if (pInput->bmiHeader.biPlanes != 1) {
  2060.         NOTE("Number of planes not one");
  2061.         return FALSE;
  2062.     }
  2063.  
  2064.     // Check the image size is consistent (it can be zero)
  2065.  
  2066.     if (pInput->bmiHeader.biSizeImage != GetBitmapSize(&pInput->bmiHeader)) {
  2067.         if (pInput->bmiHeader.biSizeImage) {
  2068.             NOTE("Image size incorrectly set");
  2069.             return FALSE;
  2070.         }
  2071.     }
  2072.  
  2073.     // Check the size of the structure
  2074.  
  2075.     if (pInput->bmiHeader.biSize != sizeof(BITMAPINFOHEADER)) {
  2076.         NOTE("Size of BITMAPINFOHEADER wrong");
  2077.         return FALSE;
  2078.     }
  2079.     return CheckPaletteHeader(pInput);
  2080. }
  2081.  
  2082.  
  2083. // This runs a few simple tests against the palette fields in the input to
  2084. // see if it looks vaguely correct. The tests look at the number of palette
  2085. // colours present, the number considered important and the biCompression
  2086. // field which should always be BI_RGB as no other formats are meaningful
  2087.  
  2088. BOOL CImageDisplay::CheckPaletteHeader(const VIDEOINFO *pInput)
  2089. {
  2090.     CAutoLock cDisplayLock(this);
  2091.  
  2092.     // The checks here are for palettised videos only
  2093.  
  2094.     if (PALETTISED(pInput) == FALSE) {
  2095.         if (pInput->bmiHeader.biClrUsed) {
  2096.             NOTE("Invalid palette entries");
  2097.             return FALSE;
  2098.         }
  2099.         return TRUE;
  2100.     }
  2101.  
  2102.     // Compression type of BI_BITFIELDS is meaningless for palette video
  2103.  
  2104.     if (pInput->bmiHeader.biCompression != BI_RGB) {
  2105.         NOTE("Palettised video must be BI_RGB");
  2106.         return FALSE;
  2107.     }
  2108.  
  2109.     // Check the number of palette colours is correct
  2110.  
  2111.     if (pInput->bmiHeader.biClrUsed > PALETTE_ENTRIES(pInput)) {
  2112.         NOTE("Too many colours in palette");
  2113.         return FALSE;
  2114.     }
  2115.  
  2116.     // The number of important colours shouldn't exceed the number used
  2117.  
  2118.     if (pInput->bmiHeader.biClrImportant > pInput->bmiHeader.biClrUsed) {
  2119.         NOTE("Too many important colours");
  2120.         return FALSE;
  2121.     }
  2122.     return TRUE;
  2123. }
  2124.  
  2125.  
  2126. // Return the format of the video display
  2127.  
  2128. const VIDEOINFO *CImageDisplay::GetDisplayFormat()
  2129. {
  2130.     CAutoLock cObjectLock(this);
  2131.     return &m_Display;
  2132. }
  2133.  
  2134.  
  2135. // Return TRUE if the display uses a palette
  2136.  
  2137. BOOL CImageDisplay::IsPalettised()
  2138. {
  2139.     CAutoLock cDisplayLock(this);
  2140.     return PALETTISED(&m_Display);
  2141. }
  2142.  
  2143.  
  2144. // Return the bit depth of the current display setting
  2145.  
  2146. WORD CImageDisplay::GetDisplayDepth()
  2147. {
  2148.     CAutoLock cDisplayLock(this);
  2149.     return m_Display.bmiHeader.biBitCount;
  2150. }
  2151.  
  2152.  
  2153. // Initialise the optional fields in a VIDEOINFO. These are mainly to do with
  2154. // the source and destination rectangles and palette information such as the
  2155. // number of colours present. It simplifies our code just a little if we don't
  2156. // have to keep checking for all the different valid permutations in a header
  2157. // every time we want to do anything with it (an example would be creating a
  2158. // palette). We set the base class media type before calling this function so
  2159. // that the media types between the pins match after a connection is made
  2160.  
  2161. HRESULT CImageDisplay::UpdateFormat(VIDEOINFO *pVideoInfo)
  2162. {
  2163.     ASSERT(pVideoInfo);
  2164.  
  2165.     BITMAPINFOHEADER *pbmi = HEADER(pVideoInfo);
  2166.     SetRectEmpty(&pVideoInfo->rcSource);
  2167.     SetRectEmpty(&pVideoInfo->rcTarget);
  2168.  
  2169.     // Set the number of colours explicitly
  2170.  
  2171.     if (PALETTISED(pVideoInfo)) {
  2172.         if (pVideoInfo->bmiHeader.biClrUsed == 0) {
  2173.             pVideoInfo->bmiHeader.biClrUsed = PALETTE_ENTRIES(pVideoInfo);
  2174.         }
  2175.     }
  2176.  
  2177.     // The number of important colours shouldn't exceed the number used, on
  2178.     // some displays the number of important colours is not initialised when
  2179.     // retrieving the display type so we set the colours used correctly
  2180.  
  2181.     if (pVideoInfo->bmiHeader.biClrImportant > pVideoInfo->bmiHeader.biClrUsed) {
  2182.         pVideoInfo->bmiHeader.biClrImportant = PALETTE_ENTRIES(pVideoInfo);
  2183.     }
  2184.  
  2185.     // Change the image size field to be explicit
  2186.  
  2187.     if (pVideoInfo->bmiHeader.biSizeImage == 0) {
  2188.         pVideoInfo->bmiHeader.biSizeImage = GetBitmapSize(&pVideoInfo->bmiHeader);
  2189.     }
  2190.     return NOERROR;
  2191. }
  2192.  
  2193.  
  2194. // Lots of video rendering filters want code to check proposed formats are ok
  2195. // This checks the VIDEOINFO we are passed as a media type. If the media type
  2196. // is a valid media type then we return NOERROR otherwise E_INVALIDARG. Note
  2197. // however we only accept formats that can be easily displayed in the display
  2198. // so if we are on a 16 bit device we will not accept 24 bit images. The one
  2199. // complexity is that most displays draw 8 bit palettised images efficiently
  2200. // so if the format is that and the display isn't 16 colour VGA we accept it
  2201.  
  2202. HRESULT CImageDisplay::CheckVideoType(const VIDEOINFO *pInput)
  2203. {
  2204.     // First of all check the VIDEOINFO looks correct
  2205.  
  2206.     if (CheckHeaderValidity(pInput) == FALSE) {
  2207.         return E_INVALIDARG;
  2208.     }
  2209.  
  2210.     // Virtually all devices support palettised images efficiently
  2211.  
  2212.     if (m_Display.bmiHeader.biBitCount >= pInput->bmiHeader.biBitCount) {
  2213.         if (PALETTISED(pInput) == TRUE) {
  2214.             NOTE("(Video) Type connection ACCEPTED");
  2215.             return NOERROR;
  2216.         }
  2217.     }
  2218.  
  2219.     // Do the colour depths match
  2220.  
  2221.     if (m_Display.bmiHeader.biBitCount != pInput->bmiHeader.biBitCount) {
  2222.         NOTE("Colour depth mismatch");
  2223.         return E_INVALIDARG;
  2224.     }
  2225.  
  2226.     // Both input and display formats are either BI_RGB or BI_BITFIELDS
  2227.  
  2228.     ASSERT(PALETTISED(pInput) == FALSE);
  2229.     ASSERT(PALETTISED(&m_Display) == FALSE);
  2230.  
  2231.     // BI_RGB 16 bit representation is implicitly RGB555, and likewise BI_RGB
  2232.     // 24 bit representation is RGB888. So we initialise a pointer to the bit
  2233.     // fields they really mean and check against the display device format
  2234.  
  2235.     const DWORD *pInputMask = GetBitMasks(pInput);
  2236.     const DWORD *pDisplayMask = GetBitMasks(&m_Display);
  2237.  
  2238.     if (pInputMask[iRED] != pDisplayMask[iRED] ||
  2239.             pInputMask[iGREEN] != pDisplayMask[iGREEN] ||
  2240.                 pInputMask[iBLUE] != pDisplayMask[iBLUE]) {
  2241.  
  2242.         NOTE("(Video) Bit field mismatch");
  2243.         return E_INVALIDARG;
  2244.     }
  2245.  
  2246.     NOTE("(Video) Type connection ACCEPTED");
  2247.     return NOERROR;
  2248. }
  2249.  
  2250.  
  2251. // Return the bit masks for the true colour VIDEOINFO provided
  2252.  
  2253. const DWORD *CImageDisplay::GetBitMasks(const VIDEOINFO *pVideoInfo)
  2254. {
  2255.     static DWORD FailMasks[] = {0,0,0};
  2256.  
  2257.     if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {
  2258.         return pVideoInfo->dwBitMasks;
  2259.     }
  2260.  
  2261.     ASSERT(pVideoInfo->bmiHeader.biCompression == BI_RGB);
  2262.  
  2263.     switch (pVideoInfo->bmiHeader.biBitCount) {
  2264.         case 16: return bits555;
  2265.         case 24: return bits888;
  2266.         case 32: return bits888;
  2267.         default: return FailMasks;
  2268.     }
  2269. }
  2270.  
  2271.  
  2272. // Check to see if we can support media type pmtIn as proposed by the output
  2273. // pin - We first check that the major media type is video and also identify
  2274. // the media sub type. Then we thoroughly check the VIDEOINFO type provided
  2275. // As well as the contained VIDEOINFO being correct the major type must be
  2276. // video, the subtype a recognised video format and the type GUID correct
  2277.  
  2278. HRESULT CImageDisplay::CheckMediaType(const CMediaType *pmtIn)
  2279. {
  2280.     // Does this have a VIDEOINFO format block
  2281.  
  2282.     const GUID *pFormatType = pmtIn->FormatType();
  2283.     if (*pFormatType != FORMAT_VideoInfo) {
  2284.         NOTE("Format GUID not a VIDEOINFO");
  2285.         return E_INVALIDARG;
  2286.     }
  2287.  
  2288.     // Check the format looks reasonably ok
  2289.  
  2290.     ULONG Length = pmtIn->FormatLength();
  2291.     if (Length < SIZE_VIDEOHEADER) {
  2292.         NOTE("Format smaller than a VIDEOHEADER");
  2293.         return E_FAIL;
  2294.     }
  2295.  
  2296.     VIDEOINFO *pInput = (VIDEOINFO *) pmtIn->Format();
  2297.  
  2298.     // Check the major type is MEDIATYPE_Video
  2299.  
  2300.     const GUID *pMajorType = pmtIn->Type();
  2301.     if (*pMajorType != MEDIATYPE_Video) {
  2302.         NOTE("Major type not MEDIATYPE_Video");
  2303.         return E_INVALIDARG;
  2304.     }
  2305.  
  2306.     // Check we can identify the media subtype
  2307.  
  2308.     const GUID *pSubType = pmtIn->Subtype();
  2309.     if (GetBitCount(pSubType) == USHRT_MAX) {
  2310.         NOTE("Invalid video media subtype");
  2311.         return E_INVALIDARG;
  2312.     }
  2313.     return CheckVideoType(pInput);
  2314. }
  2315.  
  2316.  
  2317. // Given a video format described by a VIDEOINFO structure we return the mask
  2318. // that is used to obtain the range of acceptable colours for this type, for
  2319. // example, the mask for a 24 bit true colour format is 0xFF in all cases. A
  2320. // 16 bit 5:6:5 display format uses 0xF8, 0xFC and 0xF8, therefore given any
  2321. // RGB triplets we can AND them with these fields to find one that is valid
  2322.  
  2323. BOOL CImageDisplay::GetColourMask(DWORD *pMaskRed,
  2324.                                   DWORD *pMaskGreen,
  2325.                                   DWORD *pMaskBlue)
  2326. {
  2327.     CAutoLock cDisplayLock(this);
  2328.     *pMaskRed = 0xFF;
  2329.     *pMaskGreen = 0xFF;
  2330.     *pMaskBlue = 0xFF;
  2331.  
  2332.     // If this format is palettised then it doesn't have bit fields
  2333.  
  2334.     if (m_Display.bmiHeader.biBitCount < 16) {
  2335.         return FALSE;
  2336.     }
  2337.  
  2338.     // If this is a 24 bit true colour display then it can handle all the
  2339.     // possible colour component ranges described by a byte. It is never
  2340.     // allowed for a 24 bit colour depth image to have BI_BITFIELDS set
  2341.  
  2342.     if (m_Display.bmiHeader.biBitCount == 24) {
  2343.         ASSERT(m_Display.bmiHeader.biCompression == BI_RGB);
  2344.         return TRUE;
  2345.     }
  2346.  
  2347.     // Calculate the mask based on the format's bit fields
  2348.  
  2349.     const DWORD *pBitFields = (DWORD *) GetBitMasks(&m_Display);
  2350.     DWORD *pOutputMask[] = { pMaskRed, pMaskGreen, pMaskBlue };
  2351.  
  2352.     // We know from earlier testing that there are no more than iMAXBITS
  2353.     // bits set in the mask and that they are all contiguous. All that
  2354.     // therefore remains is to shift them into the correct position
  2355.  
  2356.     for (INT iColour = iRED;iColour <= iBLUE;iColour++) {
  2357.  
  2358.         // This works out how many bits there are and where they live
  2359.  
  2360.         DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);
  2361.         DWORD SetBits = CountSetBits(pBitFields[iColour]);
  2362.  
  2363.         // The first shift moves the bit field so that it is right justified
  2364.         // in the DWORD, after which we then shift it back left which then
  2365.         // puts the leading bit in the bytes most significant bit position
  2366.  
  2367.         *(pOutputMask[iColour]) = pBitFields[iColour] >> PrefixBits;
  2368.         *(pOutputMask[iColour]) <<= (iMAXBITS - SetBits);
  2369.     }
  2370.     return TRUE;
  2371. }
  2372.  
  2373.  
  2374.  
  2375.