winutil.cpp
上传用户:liguizhu
上传日期:2015-11-01
资源大小:2422k
文件大小:92k
源码类别:

P2P编程

开发平台:

Visual C++

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