Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2693 lines
90 KiB

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