home *** CD-ROM | disk | FTP | other *** search
/ Isometric Game Programming with DirectX 7.0 / Isometric Game Programming.iso / directx / dxf / samples / multimedia / directshow / baseclasses / winutil.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  90.3 KB  |  2,585 lines

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