Support unrar64.dll
[xy_vsfilter.git] / src / filters / BaseClasses / winutil.cpp
blob9659004ac16c57d362eae92d9ea04ca573800c58
1 //------------------------------------------------------------------------------
2 // File: WinUtil.cpp
3 //
4 // Desc: DirectShow base classes - implements generic window handler class.
5 //
6 // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
10 #include <streams.h>
11 #include <limits.h>
12 #include <dvdmedia.h>
14 static UINT MsgDestroy;
16 // 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)
38 m_bDoGetDC = bDoGetDC;
42 // Prepare a window by spinning off a worker thread to do the creation and
43 // also poll the message input queue. We leave this to be called by derived
44 // classes because they might want to override methods like MessageLoop and
45 // InitialiseWindow, if we do this during construction they'll ALWAYS call
46 // this base class methods. We make the worker thread create the window so
47 // it owns it rather than the filter graph thread which is constructing us
49 HRESULT CBaseWindow::PrepareWindow()
51 if (m_hwnd) return NOERROR;
52 ASSERT(m_hwnd == NULL);
53 ASSERT(m_hdc == NULL);
55 // Get the derived object's window and class styles
57 m_pClassName = GetClassWindowStyles(&m_ClassStyles,
58 &m_WindowStyles,
59 &m_WindowStylesEx);
60 if (m_pClassName == NULL) {
61 return E_FAIL;
64 // Register our special private messages
65 m_ShowStageMessage = RegisterWindowMessage(SHOWSTAGE);
67 // RegisterWindowMessage() returns 0 if an error occurs.
68 if (0 == m_ShowStageMessage) {
69 return AmGetLastErrorToHResult();
72 m_ShowStageTop = RegisterWindowMessage(SHOWSTAGETOP);
73 if (0 == m_ShowStageTop) {
74 return AmGetLastErrorToHResult();
77 m_RealizePalette = RegisterWindowMessage(REALIZEPALETTE);
78 if (0 == m_RealizePalette) {
79 return AmGetLastErrorToHResult();
82 MsgDestroy = RegisterWindowMessage(TEXT("AM_DESTROY"));
83 if (0 == MsgDestroy) {
84 return AmGetLastErrorToHResult();
87 return DoCreateWindow();
91 // Destructor just a placeholder so that we know it becomes virtual
92 // Derived classes MUST call DoneWithWindow in their destructors so
93 // that no messages arrive after the derived class constructor ends
95 #ifdef DEBUG
96 CBaseWindow::~CBaseWindow()
98 ASSERT(m_hwnd == NULL);
99 ASSERT(m_hdc == NULL);
101 #endif
104 // We use the sync worker event to have the window destroyed. All we do is
105 // signal the event and wait on the window thread handle. Trying to send it
106 // messages causes too many problems, furthermore to be on the safe side we
107 // just wait on the thread handle while it returns WAIT_TIMEOUT or there is
108 // a sent message to process on this thread. If the constructor failed to
109 // create the thread in the first place then the loop will get terminated
111 HRESULT CBaseWindow::DoneWithWindow()
113 if (!IsWindow(m_hwnd) || (GetWindowThreadProcessId(m_hwnd, NULL) != GetCurrentThreadId())) {
115 if (IsWindow(m_hwnd)) {
117 if (m_bDoPostToDestroy) {
119 CAMEvent m_evDone;
121 // We must post a message to destroy the window
122 // That way we can't be in the middle of processing a
123 // message posted to our window when we do go away
124 // Sending a message gives less synchronization.
125 PostMessage(m_hwnd, MsgDestroy, (WPARAM)(HANDLE)m_evDone, 0);
126 WaitDispatchingMessages(m_evDone, INFINITE);
127 } else {
128 SendMessage(m_hwnd, MsgDestroy, 0, 0);
133 // This is not a leak, the window manager automatically free's
134 // hdc's that were got via GetDC, which is the case here.
135 // We set it to NULL so that we don't get any asserts later.
137 m_hdc = NULL;
140 // We need to free this DC though because USER32 does not know
141 // anything about it.
143 if (m_MemoryDC)
145 EXECUTE_ASSERT(DeleteDC(m_MemoryDC));
146 m_MemoryDC = NULL;
149 // Reset the window variables
150 m_hwnd = NULL;
152 return NOERROR;
154 const HWND hwnd = m_hwnd;
155 if (hwnd == NULL) {
156 return NOERROR;
159 InactivateWindow();
160 NOTE("Inactivated");
162 // Reset the window styles before destruction
164 SetWindowLong(hwnd,GWL_STYLE,m_WindowStyles);
165 ASSERT(GetParent(hwnd) == NULL);
166 NOTE1("Reset window styles %d",m_WindowStyles);
168 // UnintialiseWindow sets m_hwnd to NULL so save a copy
169 UninitialiseWindow();
170 DbgLog((LOG_TRACE, 2, TEXT("Destroying 0x%8.8X"), hwnd));
171 if (!DestroyWindow(hwnd)) {
172 DbgLog((LOG_TRACE, 0, TEXT("DestroyWindow %8.8X failed code %d"),
173 hwnd, GetLastError()));
174 DbgBreak("");
177 // Reset our state so we can be prepared again
179 m_pClassName = NULL;
180 m_ClassStyles = 0;
181 m_WindowStyles = 0;
182 m_WindowStylesEx = 0;
183 m_ShowStageMessage = 0;
184 m_ShowStageTop = 0;
186 return NOERROR;
190 // Called at the end to put the window in an inactive state. The pending list
191 // will always have been cleared by this time so event if the worker thread
192 // gets has been signaled and gets in to render something it will find both
193 // the state has been changed and that there are no available sample images
194 // Since we wait on the window thread to complete we don't lock the object
196 HRESULT CBaseWindow::InactivateWindow()
198 // Has the window been activated
199 if (m_bActivated == FALSE) {
200 return S_FALSE;
203 m_bActivated = FALSE;
204 ShowWindow(m_hwnd,SW_HIDE);
205 return NOERROR;
209 HRESULT CBaseWindow::CompleteConnect()
211 m_bActivated = FALSE;
212 return NOERROR;
215 // This displays a normal window. We ask the base window class for default
216 // sizes which unless overriden will return DEFWIDTH and DEFHEIGHT. We go
217 // through a couple of extra hoops to get the client area the right size
218 // as the object specifies which accounts for the AdjustWindowRectEx calls
219 // We also DWORD align the left and top coordinates of the window here to
220 // maximise the chance of being able to use DCI/DirectDraw primary surface
222 HRESULT CBaseWindow::ActivateWindow()
224 // Has the window been sized and positioned already
226 if (m_bActivated == TRUE || GetParent(m_hwnd) != NULL) {
228 SetWindowPos(m_hwnd, // Our window handle
229 HWND_TOP, // Put it at the top
230 0, 0, 0, 0, // Leave in current position
231 SWP_NOMOVE | // Don't change it's place
232 SWP_NOSIZE); // Change Z-order only
234 m_bActivated = TRUE;
235 return S_FALSE;
238 // Calculate the desired client rectangle
240 RECT WindowRect, ClientRect = GetDefaultRect();
241 GetWindowRect(m_hwnd,&WindowRect);
242 AdjustWindowRectEx(&ClientRect,GetWindowLong(m_hwnd,GWL_STYLE),
243 FALSE,GetWindowLong(m_hwnd,GWL_EXSTYLE));
245 // Align left and top edges on DWORD boundaries
247 UINT WindowFlags = (SWP_NOACTIVATE | SWP_FRAMECHANGED);
248 WindowRect.left -= (WindowRect.left & 3);
249 WindowRect.top -= (WindowRect.top & 3);
251 SetWindowPos(m_hwnd, // Window handle
252 HWND_TOP, // Put it at the top
253 WindowRect.left, // Align left edge
254 WindowRect.top, // And also top place
255 WIDTH(&ClientRect), // Horizontal size
256 HEIGHT(&ClientRect), // Vertical size
257 WindowFlags); // Don't show window
259 m_bActivated = TRUE;
260 return NOERROR;
264 // This can be used to DWORD align the window for maximum performance
266 HRESULT CBaseWindow::PerformanceAlignWindow()
268 RECT ClientRect,WindowRect;
269 GetWindowRect(m_hwnd,&WindowRect);
270 ASSERT(m_bActivated == TRUE);
272 // Don't do this if we're owned
274 if (GetParent(m_hwnd)) {
275 return NOERROR;
278 // Align left and top edges on DWORD boundaries
280 GetClientRect(m_hwnd, &ClientRect);
281 MapWindowPoints(m_hwnd, HWND_DESKTOP, (LPPOINT) &ClientRect, 2);
282 WindowRect.left -= (ClientRect.left & 3);
283 WindowRect.top -= (ClientRect.top & 3);
284 UINT WindowFlags = (SWP_NOACTIVATE | SWP_NOSIZE);
286 SetWindowPos(m_hwnd, // Window handle
287 HWND_TOP, // Put it at the top
288 WindowRect.left, // Align left edge
289 WindowRect.top, // And also top place
290 (int) 0,(int) 0, // Ignore these sizes
291 WindowFlags); // Don't show window
293 return NOERROR;
297 // Install a palette into the base window - we may be called by a different
298 // thread to the one that owns the window. We have to be careful how we do
299 // the palette realisation as we could be a different thread to the window
300 // which would cause an inter thread send message. Therefore we realise the
301 // palette by sending it a special message but without the window locked
303 HRESULT CBaseWindow::SetPalette(HPALETTE hPalette)
305 // We must own the window lock during the change
307 CAutoLock cWindowLock(&m_WindowLock);
308 CAutoLock cPaletteLock(&m_PaletteLock);
309 ASSERT(hPalette);
310 m_hPalette = hPalette;
312 return SetPalette();
316 HRESULT CBaseWindow::SetPalette()
318 if (!m_bNoRealize) {
319 SendMessage(m_hwnd, m_RealizePalette, 0, 0);
320 return S_OK;
321 } else {
322 // Just select the palette
323 ASSERT(m_hdc);
324 ASSERT(m_MemoryDC);
326 CAutoLock cPaletteLock(&m_PaletteLock);
327 SelectPalette(m_hdc,m_hPalette,m_bBackground);
328 SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);
330 return S_OK;
335 void CBaseWindow::UnsetPalette()
337 CAutoLock cWindowLock(&m_WindowLock);
338 CAutoLock cPaletteLock(&m_PaletteLock);
340 // Get a standard VGA colour palette
342 HPALETTE hPalette = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
343 ASSERT(hPalette);
345 SelectPalette(GetWindowHDC(), hPalette, TRUE);
346 SelectPalette(GetMemoryHDC(), hPalette, TRUE);
348 m_hPalette = NULL;
352 void CBaseWindow::LockPaletteLock()
354 m_PaletteLock.Lock();
358 void CBaseWindow::UnlockPaletteLock()
360 m_PaletteLock.Unlock();
364 // Realise our palettes in the window and device contexts
366 HRESULT CBaseWindow::DoRealisePalette(BOOL bForceBackground)
369 CAutoLock cPaletteLock(&m_PaletteLock);
371 if (m_hPalette == NULL) {
372 return NOERROR;
375 // Realize the palette on the window thread
376 ASSERT(m_hdc);
377 ASSERT(m_MemoryDC);
379 SelectPalette(m_hdc,m_hPalette,m_bBackground || bForceBackground);
380 SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);
383 // If we grab a critical section here we can deadlock
384 // with the window thread because one of the side effects
385 // of RealizePalette is to send a WM_PALETTECHANGED message
386 // to every window in the system. In our handling
387 // of WM_PALETTECHANGED we used to grab this CS too.
388 // The really bad case is when our renderer calls DoRealisePalette()
389 // while we're in the middle of processing a palette change
390 // for another window.
391 // So don't hold the critical section while actually realising
392 // the palette. In any case USER is meant to manage palette
393 // handling - we shouldn't have to serialize everything as well
394 ASSERT(CritCheckOut(&m_WindowLock));
395 ASSERT(CritCheckOut(&m_PaletteLock));
397 EXECUTE_ASSERT(RealizePalette(m_hdc) != GDI_ERROR);
398 EXECUTE_ASSERT(RealizePalette(m_MemoryDC) != GDI_ERROR);
400 return (GdiFlush() == FALSE ? S_FALSE : S_OK);
404 // This is the global window procedure
406 LRESULT CALLBACK WndProc(HWND hwnd, // Window handle
407 UINT uMsg, // Message ID
408 WPARAM wParam, // First parameter
409 LPARAM lParam) // Other parameter
412 // Get the window long that holds our window object pointer
413 // If it is NULL then we are initialising the window in which
414 // case the object pointer has been passed in the window creation
415 // structure. IF we get any messages before WM_NCCREATE we will
416 // pass them to DefWindowProc.
418 CBaseWindow *pBaseWindow = (CBaseWindow *)GetWindowLongPtr(hwnd,0);
419 if (pBaseWindow == NULL) {
421 // Get the structure pointer from the create struct.
422 // We can only do this for WM_NCCREATE which should be one of
423 // the first messages we receive. Anything before this will
424 // have to be passed to DefWindowProc (i.e. WM_GETMINMAXINFO)
426 // If the message is WM_NCCREATE we set our pBaseWindow pointer
427 // and will then place it in the window structure
429 // turn off WS_EX_LAYOUTRTL style for quartz windows
430 if (uMsg == WM_NCCREATE) {
431 SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & ~0x400000);
434 if ((uMsg != WM_NCCREATE)
435 || (NULL == (pBaseWindow = *(CBaseWindow**) ((LPCREATESTRUCT)lParam)->lpCreateParams)))
437 return(DefWindowProc(hwnd, uMsg, wParam, lParam));
440 // Set the window LONG to be the object who created us
441 #ifdef DEBUG
442 SetLastError(0); // because of the way SetWindowLong works
443 #endif
444 LONG_PTR rc = SetWindowLongPtr(hwnd, (DWORD) 0, (LONG_PTR) pBaseWindow);
445 #ifdef DEBUG
446 if (0 == rc) {
447 // SetWindowLong MIGHT have failed. (Read the docs which admit
448 // that it is awkward to work out if you have had an error.)
449 LONG lasterror = GetLastError();
450 ASSERT(0 == lasterror);
451 // If this is not the case we have not set the pBaseWindow pointer
452 // into the window structure and we will blow up.
454 #endif
457 // See if this is the packet of death
458 if (uMsg == MsgDestroy && uMsg != 0) {
459 pBaseWindow->DoneWithWindow();
460 if (pBaseWindow->m_bDoPostToDestroy) {
461 EXECUTE_ASSERT(SetEvent((HANDLE)wParam));
463 return 0;
465 return pBaseWindow->OnReceiveMessage(hwnd,uMsg,wParam,lParam);
469 // When the window size changes we adjust our member variables that
470 // contain the dimensions of the client rectangle for our window so
471 // that we come to render an image we will know whether to stretch
473 BOOL CBaseWindow::OnSize(LONG Width, LONG Height)
475 m_Width = Width;
476 m_Height = Height;
477 return TRUE;
481 // This function handles the WM_CLOSE message
483 BOOL CBaseWindow::OnClose()
485 ShowWindow(m_hwnd,SW_HIDE);
486 return TRUE;
490 // This is called by the worker window thread when it receives a terminate
491 // message from the window object destructor to delete all the resources we
492 // allocated during initialisation. By the time the worker thread exits all
493 // processing will have been completed as the source filter disconnection
494 // flushes the image pending sample, therefore the GdiFlush should succeed
496 HRESULT CBaseWindow::UninitialiseWindow()
498 // Have we already cleaned up
500 if (m_hwnd == NULL) {
501 ASSERT(m_hdc == NULL);
502 ASSERT(m_MemoryDC == NULL);
503 return NOERROR;
506 // Release the window resources
508 EXECUTE_ASSERT(GdiFlush());
510 if (m_hdc)
512 EXECUTE_ASSERT(ReleaseDC(m_hwnd,m_hdc));
513 m_hdc = NULL;
516 if (m_MemoryDC)
518 EXECUTE_ASSERT(DeleteDC(m_MemoryDC));
519 m_MemoryDC = NULL;
522 // Reset the window variables
523 m_hwnd = NULL;
525 return NOERROR;
529 // This is called by the worker window thread after it has created the main
530 // window and it wants to initialise the rest of the owner objects window
531 // variables such as the device contexts. We execute this function with the
532 // critical section still locked. Nothing in this function must generate any
533 // SendMessage calls to the window because this is executing on the window
534 // thread so the message will never be processed and we will deadlock
536 HRESULT CBaseWindow::InitialiseWindow(HWND hwnd)
538 // Initialise the window variables
540 ASSERT(IsWindow(hwnd));
541 m_hwnd = hwnd;
543 if (m_bDoGetDC)
545 EXECUTE_ASSERT(m_hdc = GetDC(hwnd));
546 EXECUTE_ASSERT(m_MemoryDC = CreateCompatibleDC(m_hdc));
548 EXECUTE_ASSERT(SetStretchBltMode(m_hdc,COLORONCOLOR));
549 EXECUTE_ASSERT(SetStretchBltMode(m_MemoryDC,COLORONCOLOR));
552 return NOERROR;
555 HRESULT CBaseWindow::DoCreateWindow()
557 WNDCLASS wndclass; // Used to register classes
558 BOOL bRegistered; // Is this class registered
559 HWND hwnd; // Handle to our window
561 bRegistered = GetClassInfo(m_hInstance, // Module instance
562 m_pClassName, // Window class
563 &wndclass); // Info structure
565 // if the window is to be used for drawing puposes and we are getting a DC
566 // for the entire lifetime of the window then changes the class style to do
567 // say so. If we don't set this flag then the DC comes from the cache and is
568 // really bad.
569 if (m_bDoGetDC)
571 m_ClassStyles |= CS_OWNDC;
574 if (bRegistered == FALSE) {
576 // Register the renderer window class
578 wndclass.lpszClassName = m_pClassName;
579 wndclass.style = m_ClassStyles;
580 wndclass.lpfnWndProc = WndProc;
581 wndclass.cbClsExtra = 0;
582 wndclass.cbWndExtra = sizeof(CBaseWindow *);
583 wndclass.hInstance = m_hInstance;
584 wndclass.hIcon = NULL;
585 wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
586 wndclass.hbrBackground = (HBRUSH) NULL;
587 wndclass.lpszMenuName = NULL;
589 RegisterClass(&wndclass);
592 // Create the frame window. Pass the pBaseWindow information in the
593 // CreateStruct which allows our message handling loop to get hold of
594 // the pBaseWindow pointer.
596 CBaseWindow *pBaseWindow = this; // The owner window object
597 hwnd = CreateWindowEx(m_WindowStylesEx, // Extended styles
598 m_pClassName, // Registered name
599 TEXT("ActiveMovie Window"), // Window title
600 m_WindowStyles, // Window styles
601 CW_USEDEFAULT, // Start x position
602 CW_USEDEFAULT, // Start y position
603 DEFWIDTH, // Window width
604 DEFHEIGHT, // Window height
605 NULL, // Parent handle
606 NULL, // Menu handle
607 m_hInstance, // Instance handle
608 &pBaseWindow); // Creation data
610 // If we failed signal an error to the object constructor (based on the
611 // last Win32 error on this thread) then signal the constructor thread
612 // to continue, release the mutex to let others have a go and exit
614 if (hwnd == NULL) {
615 DWORD Error = GetLastError();
616 return AmHresultFromWin32(Error);
619 // Check the window LONG is the object who created us
620 ASSERT(GetWindowLongPtr(hwnd, 0) == (LONG_PTR)this);
622 // Initialise the window and then signal the constructor so that it can
623 // continue and then finally unlock the object's critical section. The
624 // window class is left registered even after we terminate the thread
625 // as we don't know when the last window has been closed. So we allow
626 // the operating system to free the class resources as appropriate
628 InitialiseWindow(hwnd);
630 DbgLog((LOG_TRACE, 2, TEXT("Created window class (%s) HWND(%8.8X)"),
631 m_pClassName, hwnd));
633 return S_OK;
637 // The base class provides some default handling and calls DefWindowProc
639 LRESULT CBaseWindow::OnReceiveMessage(HWND hwnd, // Window handle
640 UINT uMsg, // Message ID
641 WPARAM wParam, // First parameter
642 LPARAM lParam) // Other parameter
644 ASSERT(IsWindow(hwnd));
646 if (PossiblyEatMessage(uMsg, wParam, lParam))
647 return 0;
649 // This is sent by the IVideoWindow SetWindowForeground method. If the
650 // window is invisible we will show it and make it topmost without the
651 // foreground focus. If the window is visible it will also be made the
652 // topmost window without the foreground focus. If wParam is TRUE then
653 // for both cases the window will be forced into the foreground focus
655 if (uMsg == m_ShowStageMessage) {
657 BOOL bVisible = IsWindowVisible(hwnd);
658 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
659 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW |
660 (bVisible ? SWP_NOACTIVATE : 0));
662 // Should we bring the window to the foreground
663 if (wParam == TRUE) {
664 SetForegroundWindow(hwnd);
666 return (LRESULT) 1;
669 // When we go fullscreen we have to add the WS_EX_TOPMOST style to the
670 // video window so that it comes out above any task bar (this is more
671 // relevant to WindowsNT than Windows95). However the SetWindowPos call
672 // must be on the same thread as that which created the window. The
673 // wParam parameter can be TRUE or FALSE to set and reset the topmost
675 if (uMsg == m_ShowStageTop) {
676 HWND HwndTop = (wParam == TRUE ? HWND_TOPMOST : HWND_NOTOPMOST);
677 BOOL bVisible = IsWindowVisible(hwnd);
678 SetWindowPos(hwnd, HwndTop, 0, 0, 0, 0,
679 SWP_NOMOVE | SWP_NOSIZE |
680 (wParam == TRUE ? SWP_SHOWWINDOW : 0) |
681 (bVisible ? SWP_NOACTIVATE : 0));
682 return (LRESULT) 1;
685 // New palette stuff
686 if (uMsg == m_RealizePalette) {
687 ASSERT(m_hwnd == hwnd);
688 return OnPaletteChange(m_hwnd,WM_QUERYNEWPALETTE);
691 switch (uMsg) {
693 // Repaint the window if the system colours change
695 case WM_SYSCOLORCHANGE:
697 InvalidateRect(hwnd,NULL,FALSE);
698 return (LRESULT) 1;
700 // Somebody has changed the palette
701 case WM_PALETTECHANGED:
703 OnPaletteChange((HWND)wParam,uMsg);
704 return (LRESULT) 0;
706 // We are about to receive the keyboard focus so we ask GDI to realise
707 // our logical palette again and hopefully it will be fully installed
708 // without any mapping having to be done during any picture rendering
710 case WM_QUERYNEWPALETTE:
711 ASSERT(m_hwnd == hwnd);
712 return OnPaletteChange(m_hwnd,uMsg);
714 // do NOT fwd WM_MOVE. the parameters are the location of the parent
715 // window, NOT what the renderer should be looking at. But we need
716 // to make sure the overlay is moved with the parent window, so we
717 // do this.
718 case WM_MOVE:
719 if (IsWindowVisible(m_hwnd)) {
720 PostMessage(m_hwnd,WM_PAINT,0,0);
722 break;
724 // Store the width and height as useful base class members
726 case WM_SIZE:
728 OnSize(LOWORD(lParam), HIWORD(lParam));
729 return (LRESULT) 0;
731 // Intercept the WM_CLOSE messages to hide the window
733 case WM_CLOSE:
735 OnClose();
736 return (LRESULT) 0;
738 return DefWindowProc(hwnd,uMsg,wParam,lParam);
742 // This handles the Windows palette change messages - if we do realise our
743 // palette then we return TRUE otherwise we return FALSE. If our window is
744 // foreground application then we should get first choice of colours in the
745 // system palette entries. We get best performance when our logical palette
746 // includes the standard VGA colours (at the beginning and end) otherwise
747 // GDI may have to map from our palette to the device palette while drawing
749 LRESULT CBaseWindow::OnPaletteChange(HWND hwnd,UINT Message)
751 // First check we are not changing the palette during closedown
753 if (m_hwnd == NULL || hwnd == NULL) {
754 return (LRESULT) 0;
756 ASSERT(!m_bRealizing);
758 // Should we realise our palette again
760 if ((Message == WM_QUERYNEWPALETTE || hwnd != m_hwnd)) {
761 // It seems that even if we're invisible that we can get asked
762 // to realize our palette and this can cause really ugly side-effects
763 // Seems like there's another bug but this masks it a least for the
764 // shutting down case.
765 if (!IsWindowVisible(m_hwnd)) {
766 DbgLog((LOG_TRACE, 1, TEXT("Realizing when invisible!")));
767 return (LRESULT) 0;
770 // Avoid recursion with multiple graphs in the same app
771 #ifdef DEBUG
772 m_bRealizing = TRUE;
773 #endif
774 DoRealisePalette(Message != WM_QUERYNEWPALETTE);
775 #ifdef DEBUG
776 m_bRealizing = FALSE;
777 #endif
779 // Should we redraw the window with the new palette
780 if (Message == WM_PALETTECHANGED) {
781 InvalidateRect(m_hwnd,NULL,FALSE);
785 return (LRESULT) 1;
789 // Determine if the window exists.
791 bool CBaseWindow::WindowExists()
793 return !!IsWindow(m_hwnd);
797 // Return the default window rectangle
799 RECT CBaseWindow::GetDefaultRect()
801 RECT DefaultRect = {0,0,DEFWIDTH,DEFHEIGHT};
802 ASSERT(m_hwnd);
803 // ASSERT(m_hdc);
804 return DefaultRect;
808 // Return the current window width
810 LONG CBaseWindow::GetWindowWidth()
812 ASSERT(m_hwnd);
813 // ASSERT(m_hdc);
814 return m_Width;
818 // Return the current window height
820 LONG CBaseWindow::GetWindowHeight()
822 ASSERT(m_hwnd);
823 // ASSERT(m_hdc);
824 return m_Height;
828 // Return the window handle
830 HWND CBaseWindow::GetWindowHWND()
832 ASSERT(m_hwnd);
833 // ASSERT(m_hdc);
834 return m_hwnd;
838 // Return the window drawing device context
840 HDC CBaseWindow::GetWindowHDC()
842 ASSERT(m_hwnd);
843 ASSERT(m_hdc);
844 return m_hdc;
848 // Return the offscreen window drawing device context
850 HDC CBaseWindow::GetMemoryHDC()
852 ASSERT(m_hwnd);
853 ASSERT(m_MemoryDC);
854 return m_MemoryDC;
858 #ifdef DEBUG
859 HPALETTE CBaseWindow::GetPalette()
861 // The palette lock should always be held when accessing
862 // m_hPalette.
863 ASSERT(CritCheckIn(&m_PaletteLock));
864 return m_hPalette;
866 #endif // DEBUG
869 // This is available to clients who want to change the window visiblity. It's
870 // little more than an indirection to the Win32 ShowWindow although these is
871 // some benefit in going through here as this function may change sometime
873 HRESULT CBaseWindow::DoShowWindow(LONG ShowCmd)
875 ShowWindow(m_hwnd,ShowCmd);
876 return NOERROR;
880 // Generate a WM_PAINT message for the video window
882 void CBaseWindow::PaintWindow(BOOL bErase)
884 InvalidateRect(m_hwnd,NULL,bErase);
888 // Allow an application to have us set the video window in the foreground. We
889 // have this because it is difficult for one thread to do do this to a window
890 // owned by another thread. Rather than expose the message we use to execute
891 // the inter thread send message we provide the interface function. All we do
892 // is to SendMessage to the video window renderer thread with a WM_SHOWSTAGE
894 void CBaseWindow::DoSetWindowForeground(BOOL bFocus)
896 SendMessage(m_hwnd,m_ShowStageMessage,(WPARAM) bFocus,(LPARAM) 0);
900 // Constructor initialises the owning object pointer. Since we are a worker
901 // class for the main window object we have relatively few state variables to
902 // look after. We are given device context handles to use later on as well as
903 // the source and destination rectangles (but reset them here just in case)
905 CDrawImage::CDrawImage(CBaseWindow *pBaseWindow) :
906 m_pBaseWindow(pBaseWindow),
907 m_hdc(NULL),
908 m_MemoryDC(NULL),
909 m_bStretch(FALSE),
910 m_pMediaType(NULL),
911 m_bUsingImageAllocator(FALSE)
913 ASSERT(pBaseWindow);
914 ResetPaletteVersion();
915 SetRectEmpty(&m_TargetRect);
916 SetRectEmpty(&m_SourceRect);
918 m_perfidRenderTime = MSR_REGISTER(TEXT("Single Blt time"));
922 // Overlay the image time stamps on the picture. Access to this method is
923 // serialised by the caller. We display the sample start and end times on
924 // top of the video using TextOut on the device context we are handed. If
925 // there isn't enough room in the window for the times we don't show them
927 void CDrawImage::DisplaySampleTimes(IMediaSample *pSample)
929 #ifdef DEBUG
931 // Only allow the "annoying" time messages if the users has turned the
932 // logging "way up"
934 BOOL bAccept = DbgCheckModuleLevel(LOG_TRACE, 5);
935 if (bAccept == FALSE) {
936 return;
938 #endif
940 TCHAR szTimes[TIMELENGTH]; // Time stamp strings
941 ASSERT(pSample); // Quick sanity check
942 RECT ClientRect; // Client window size
943 SIZE Size; // Size of text output
945 // Get the time stamps and window size
947 pSample->GetTime((REFERENCE_TIME*)&m_StartSample, (REFERENCE_TIME*)&m_EndSample);
948 HWND hwnd = m_pBaseWindow->GetWindowHWND();
949 EXECUTE_ASSERT(GetClientRect(hwnd,&ClientRect));
951 // Format the sample time stamps
953 wsprintf(szTimes,TEXT("%08d : %08d"),
954 m_StartSample.Millisecs(),
955 m_EndSample.Millisecs());
957 ASSERT(lstrlen(szTimes) < TIMELENGTH);
959 // Put the times in the middle at the bottom of the window
961 GetTextExtentPoint32(m_hdc,szTimes,lstrlen(szTimes),&Size);
962 INT XPos = ((ClientRect.right - ClientRect.left) - Size.cx) / 2;
963 INT YPos = ((ClientRect.bottom - ClientRect.top) - Size.cy) * 4 / 5;
965 // Check the window is big enough to have sample times displayed
967 if ((XPos > 0) && (YPos > 0)) {
968 TextOut(m_hdc,XPos,YPos,szTimes,lstrlen(szTimes));
973 // This is called when the drawing code sees that the image has a down level
974 // palette cookie. We simply call the SetDIBColorTable Windows API with the
975 // palette that is found after the BITMAPINFOHEADER - we return no errors
977 void CDrawImage::UpdateColourTable(HDC hdc,BITMAPINFOHEADER *pbmi)
979 ASSERT(pbmi->biClrUsed);
980 RGBQUAD *pColourTable = (RGBQUAD *)(pbmi+1);
982 // Set the new palette in the device context
984 UINT uiReturn = SetDIBColorTable(hdc,(UINT) 0,
985 pbmi->biClrUsed,
986 pColourTable);
988 // Should always succeed but check in debug builds
989 ASSERT(uiReturn == pbmi->biClrUsed);
993 // No source rectangle scaling is done by the base class
995 RECT CDrawImage::ScaleSourceRect(const RECT *pSource)
997 ASSERT(pSource);
998 return *pSource;
1002 // This is called when the funky output pin uses our allocator. The samples we
1003 // allocate are special because the memory is shared between us and GDI thus
1004 // removing one copy when we ask for the image to be rendered. The source type
1005 // information is in the main renderer m_mtIn field which is initialised when
1006 // the media type is agreed in SetMediaType, the media type may be changed on
1007 // the fly if, for example, the source filter needs to change the palette
1009 void CDrawImage::FastRender(IMediaSample *pMediaSample)
1011 BITMAPINFOHEADER *pbmi; // Image format data
1012 DIBDATA *pDibData; // Stores DIB information
1013 BYTE *pImage; // Pointer to image data
1014 HBITMAP hOldBitmap; // Store the old bitmap
1015 CImageSample *pSample; // Pointer to C++ object
1017 ASSERT(m_pMediaType);
1019 // From the untyped source format block get the VIDEOINFO and subsequently
1020 // the BITMAPINFOHEADER structure. We can cast the IMediaSample interface
1021 // to a CImageSample object so we can retrieve it's DIBSECTION details
1023 pbmi = HEADER(m_pMediaType->Format());
1024 pSample = (CImageSample *) pMediaSample;
1025 pDibData = pSample->GetDIBData();
1026 hOldBitmap = (HBITMAP) SelectObject(m_MemoryDC,pDibData->hBitmap);
1028 // Get a pointer to the real image data
1030 HRESULT hr = pMediaSample->GetPointer(&pImage);
1031 if (FAILED(hr)) {
1032 return;
1035 // Do we need to update the colour table, we increment our palette cookie
1036 // each time we get a dynamic format change. The sample palette cookie is
1037 // stored in the DIBDATA structure so we try to keep the fields in sync
1038 // By the time we get to draw the images the format change will be done
1039 // so all we do is ask the renderer for what it's palette version is
1041 if (pDibData->PaletteVersion < GetPaletteVersion()) {
1042 ASSERT(pbmi->biBitCount <= iPALETTE);
1043 UpdateColourTable(m_MemoryDC,pbmi);
1044 pDibData->PaletteVersion = GetPaletteVersion();
1047 // This allows derived classes to change the source rectangle that we do
1048 // the drawing with. For example a renderer may ask a codec to stretch
1049 // the video from 320x240 to 640x480, in which case the source we see in
1050 // here will still be 320x240, although the source we want to draw with
1051 // should be scaled up to 640x480. The base class implementation of this
1052 // method does nothing but return the same rectangle as we are passed in
1054 RECT SourceRect = ScaleSourceRect(&m_SourceRect);
1056 // Is the window the same size as the video
1058 if (m_bStretch == FALSE) {
1060 // Put the image straight into the window
1062 BitBlt(
1063 (HDC) m_hdc, // Target device HDC
1064 m_TargetRect.left, // X sink position
1065 m_TargetRect.top, // Y sink position
1066 m_TargetRect.right - m_TargetRect.left, // Destination width
1067 m_TargetRect.bottom - m_TargetRect.top, // Destination height
1068 m_MemoryDC, // Source device context
1069 SourceRect.left, // X source position
1070 SourceRect.top, // Y source position
1071 SRCCOPY); // Simple copy
1073 } else {
1075 // Stretch the image when copying to the window
1077 StretchBlt(
1078 (HDC) m_hdc, // Target device HDC
1079 m_TargetRect.left, // X sink position
1080 m_TargetRect.top, // Y sink position
1081 m_TargetRect.right - m_TargetRect.left, // Destination width
1082 m_TargetRect.bottom - m_TargetRect.top, // Destination height
1083 m_MemoryDC, // Source device HDC
1084 SourceRect.left, // X source position
1085 SourceRect.top, // Y source position
1086 SourceRect.right - SourceRect.left, // Source width
1087 SourceRect.bottom - SourceRect.top, // Source height
1088 SRCCOPY); // Simple copy
1091 // This displays the sample times over the top of the image. This used to
1092 // draw the times into the offscreen device context however that actually
1093 // writes the text into the image data buffer which may not be writable
1095 #ifdef DEBUG
1096 DisplaySampleTimes(pMediaSample);
1097 #endif
1099 // Put the old bitmap back into the device context so we don't leak
1100 SelectObject(m_MemoryDC,hOldBitmap);
1104 // This is called when there is a sample ready to be drawn, unfortunately the
1105 // output pin was being rotten and didn't choose our super excellent shared
1106 // memory DIB allocator so we have to do this slow render using boring old GDI
1107 // SetDIBitsToDevice and StretchDIBits. The down side of using these GDI
1108 // functions is that the image data has to be copied across from our address
1109 // space into theirs before going to the screen (although in reality the cost
1110 // is small because all they do is to map the buffer into their address space)
1112 void CDrawImage::SlowRender(IMediaSample *pMediaSample)
1114 // Get the BITMAPINFOHEADER for the connection
1116 ASSERT(m_pMediaType);
1117 BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format());
1118 BYTE *pImage;
1120 // Get the image data buffer
1122 HRESULT hr = pMediaSample->GetPointer(&pImage);
1123 if (FAILED(hr)) {
1124 return;
1127 // This allows derived classes to change the source rectangle that we do
1128 // the drawing with. For example a renderer may ask a codec to stretch
1129 // the video from 320x240 to 640x480, in which case the source we see in
1130 // here will still be 320x240, although the source we want to draw with
1131 // should be scaled up to 640x480. The base class implementation of this
1132 // method does nothing but return the same rectangle as we are passed in
1134 RECT SourceRect = ScaleSourceRect(&m_SourceRect);
1136 LONG lAdjustedSourceTop = SourceRect.top;
1137 // if the origin of bitmap is bottom-left, adjust soruce_rect_top
1138 // to be the bottom-left corner instead of the top-left.
1139 if (pbmi->biHeight > 0) {
1140 lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom;
1142 // Is the window the same size as the video
1144 if (m_bStretch == FALSE) {
1146 // Put the image straight into the window
1148 SetDIBitsToDevice(
1149 (HDC) m_hdc, // Target device HDC
1150 m_TargetRect.left, // X sink position
1151 m_TargetRect.top, // Y sink position
1152 m_TargetRect.right - m_TargetRect.left, // Destination width
1153 m_TargetRect.bottom - m_TargetRect.top, // Destination height
1154 SourceRect.left, // X source position
1155 lAdjustedSourceTop, // Adjusted Y source position
1156 (UINT) 0, // Start scan line
1157 pbmi->biHeight, // Scan lines present
1158 pImage, // Image data
1159 (BITMAPINFO *) pbmi, // DIB header
1160 DIB_RGB_COLORS); // Type of palette
1162 } else {
1164 // Stretch the image when copying to the window
1166 StretchDIBits(
1167 (HDC) m_hdc, // Target device HDC
1168 m_TargetRect.left, // X sink position
1169 m_TargetRect.top, // Y sink position
1170 m_TargetRect.right - m_TargetRect.left, // Destination width
1171 m_TargetRect.bottom - m_TargetRect.top, // Destination height
1172 SourceRect.left, // X source position
1173 lAdjustedSourceTop, // Adjusted Y source position
1174 SourceRect.right - SourceRect.left, // Source width
1175 SourceRect.bottom - SourceRect.top, // Source height
1176 pImage, // Image data
1177 (BITMAPINFO *) pbmi, // DIB header
1178 DIB_RGB_COLORS, // Type of palette
1179 SRCCOPY); // Simple image copy
1182 // This shows the sample reference times over the top of the image which
1183 // looks a little flickery. I tried using GdiSetBatchLimit and GdiFlush to
1184 // control the screen updates but it doesn't quite work as expected and
1185 // only partially reduces the flicker. I also tried using a memory context
1186 // and combining the two in that before doing a final BitBlt operation to
1187 // the screen, unfortunately this has considerable performance penalties
1188 // and also means that this code is not executed when compiled retail
1190 #ifdef DEBUG
1191 DisplaySampleTimes(pMediaSample);
1192 #endif
1196 // This is called with an IMediaSample interface on the image to be drawn. We
1197 // decide on the drawing mechanism based on who's allocator we are using. We
1198 // may be called when the window wants an image painted by WM_PAINT messages
1199 // We can't realise the palette here because we have the renderer lock, any
1200 // call to realise may cause an interthread send message to the window thread
1201 // which may in turn be waiting to get the renderer lock before servicing it
1203 BOOL CDrawImage::DrawImage(IMediaSample *pMediaSample)
1205 ASSERT(m_hdc);
1206 ASSERT(m_MemoryDC);
1207 NotifyStartDraw();
1209 // If the output pin used our allocator then the samples passed are in
1210 // fact CVideoSample objects that contain CreateDIBSection data that we
1211 // use to do faster image rendering, they may optionally also contain a
1212 // DirectDraw surface pointer in which case we do not do the drawing
1214 if (m_bUsingImageAllocator == FALSE) {
1215 SlowRender(pMediaSample);
1216 EXECUTE_ASSERT(GdiFlush());
1217 NotifyEndDraw();
1218 return TRUE;
1221 // This is a DIBSECTION buffer
1223 FastRender(pMediaSample);
1224 EXECUTE_ASSERT(GdiFlush());
1225 NotifyEndDraw();
1226 return TRUE;
1230 BOOL CDrawImage::DrawVideoImageHere(
1231 HDC hdc,
1232 IMediaSample *pMediaSample,
1233 LPRECT lprcSrc,
1234 LPRECT lprcDst
1237 ASSERT(m_pMediaType);
1238 BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format());
1239 BYTE *pImage;
1241 // Get the image data buffer
1243 HRESULT hr = pMediaSample->GetPointer(&pImage);
1244 if (FAILED(hr)) {
1245 return FALSE;
1248 RECT SourceRect;
1249 RECT TargetRect;
1251 if (lprcSrc) {
1252 SourceRect = *lprcSrc;
1254 else SourceRect = ScaleSourceRect(&m_SourceRect);
1256 if (lprcDst) {
1257 TargetRect = *lprcDst;
1259 else TargetRect = m_TargetRect;
1261 LONG lAdjustedSourceTop = SourceRect.top;
1262 // if the origin of bitmap is bottom-left, adjust soruce_rect_top
1263 // to be the bottom-left corner instead of the top-left.
1264 if (pbmi->biHeight > 0) {
1265 lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom;
1269 // Stretch the image when copying to the DC
1271 BOOL bRet = (0 != StretchDIBits(hdc,
1272 TargetRect.left,
1273 TargetRect.top,
1274 TargetRect.right - TargetRect.left,
1275 TargetRect.bottom - TargetRect.top,
1276 SourceRect.left,
1277 lAdjustedSourceTop,
1278 SourceRect.right - SourceRect.left,
1279 SourceRect.bottom - SourceRect.top,
1280 pImage,
1281 (BITMAPINFO *)pbmi,
1282 DIB_RGB_COLORS,
1283 SRCCOPY));
1284 return bRet;
1288 // This is called by the owning window object after it has created the window
1289 // and it's drawing contexts. We are constructed with the base window we'll
1290 // be drawing into so when given the notification we retrive the device HDCs
1291 // to draw with. We cannot call these in our constructor as they are virtual
1293 void CDrawImage::SetDrawContext()
1295 m_MemoryDC = m_pBaseWindow->GetMemoryHDC();
1296 m_hdc = m_pBaseWindow->GetWindowHDC();
1300 // This is called to set the target rectangle in the video window, it will be
1301 // called whenever a WM_SIZE message is retrieved from the message queue. We
1302 // simply store the rectangle and use it later when we do the drawing calls
1304 void CDrawImage::SetTargetRect(RECT *pTargetRect)
1306 ASSERT(pTargetRect);
1307 m_TargetRect = *pTargetRect;
1308 SetStretchMode();
1312 // Return the current target rectangle
1314 void CDrawImage::GetTargetRect(RECT *pTargetRect)
1316 ASSERT(pTargetRect);
1317 *pTargetRect = m_TargetRect;
1321 // This is called when we want to change the section of the image to draw. We
1322 // use this information in the drawing operation calls later on. We must also
1323 // see if the source and destination rectangles have the same dimensions. If
1324 // not we must stretch during the drawing rather than a direct pixel copy
1326 void CDrawImage::SetSourceRect(RECT *pSourceRect)
1328 ASSERT(pSourceRect);
1329 m_SourceRect = *pSourceRect;
1330 SetStretchMode();
1334 // Return the current source rectangle
1336 void CDrawImage::GetSourceRect(RECT *pSourceRect)
1338 ASSERT(pSourceRect);
1339 *pSourceRect = m_SourceRect;
1343 // This is called when either the source or destination rectanges change so we
1344 // can update the stretch flag. If the rectangles don't match we stretch the
1345 // video during the drawing otherwise we call the fast pixel copy functions
1346 // NOTE the source and/or the destination rectangle may be completely empty
1348 void CDrawImage::SetStretchMode()
1350 // Calculate the overall rectangle dimensions
1352 LONG SourceWidth = m_SourceRect.right - m_SourceRect.left;
1353 LONG SinkWidth = m_TargetRect.right - m_TargetRect.left;
1354 LONG SourceHeight = m_SourceRect.bottom - m_SourceRect.top;
1355 LONG SinkHeight = m_TargetRect.bottom - m_TargetRect.top;
1357 m_bStretch = TRUE;
1358 if (SourceWidth == SinkWidth) {
1359 if (SourceHeight == SinkHeight) {
1360 m_bStretch = FALSE;
1366 // Tell us whose allocator we are using. This should be called with TRUE if
1367 // the filter agrees to use an allocator based around the CImageAllocator
1368 // SDK base class - whose image buffers are made through CreateDIBSection.
1369 // Otherwise this should be called with FALSE and we will draw the images
1370 // using SetDIBitsToDevice and StretchDIBitsToDevice. None of these calls
1371 // can handle buffers which have non zero strides (like DirectDraw uses)
1373 void CDrawImage::NotifyAllocator(BOOL bUsingImageAllocator)
1375 m_bUsingImageAllocator = bUsingImageAllocator;
1379 // Are we using the image DIBSECTION allocator
1381 BOOL CDrawImage::UsingImageAllocator()
1383 return m_bUsingImageAllocator;
1387 // We need the media type of the connection so that we can get the BITMAPINFO
1388 // from it. We use that in the calls to draw the image such as StretchDIBits
1389 // and also when updating the colour table held in shared memory DIBSECTIONs
1391 void CDrawImage::NotifyMediaType(CMediaType *pMediaType)
1393 m_pMediaType = pMediaType;
1397 // We store in this object a cookie maintaining the current palette version.
1398 // Each time a palettised format is changed we increment this value so that
1399 // when we come to draw the images we look at the colour table value they
1400 // have and if less than the current we know to update it. This version is
1401 // only needed and indeed used when working with shared memory DIBSECTIONs
1403 LONG CDrawImage::GetPaletteVersion()
1405 return m_PaletteVersion;
1409 // Resets the current palette version number
1411 void CDrawImage::ResetPaletteVersion()
1413 m_PaletteVersion = PALETTE_VERSION;
1417 // Increment the current palette version
1419 void CDrawImage::IncrementPaletteVersion()
1421 m_PaletteVersion++;
1425 // Constructor must initialise the base allocator. Each sample we create has a
1426 // palette version cookie on board. When the source filter changes the palette
1427 // during streaming the window object increments an internal cookie counter it
1428 // keeps as well. When it comes to render the samples it looks at the cookie
1429 // values and if they don't match then it knows to update the sample's colour
1430 // table. However we always create samples with a cookie of PALETTE_VERSION
1431 // If there have been multiple format changes and we disconnect and reconnect
1432 // thereby causing the samples to be reallocated we will create them with a
1433 // cookie much lower than the current version, this isn't a problem since it
1434 // will be seen by the window object and the versions will then be updated
1436 CImageAllocator::CImageAllocator(CBaseFilter *pFilter,
1437 TCHAR *pName,
1438 HRESULT *phr) :
1439 CBaseAllocator(pName,NULL,phr,TRUE,TRUE),
1440 m_pFilter(pFilter)
1442 ASSERT(phr);
1443 ASSERT(pFilter);
1447 // Check our DIB buffers have been released
1449 #ifdef DEBUG
1450 CImageAllocator::~CImageAllocator()
1452 ASSERT(m_bCommitted == FALSE);
1454 #endif
1457 // Called from destructor and also from base class to free resources. We work
1458 // our way through the list of media samples deleting the DIBSECTION created
1459 // for each. All samples should be back in our list so there is no chance a
1460 // filter is still using one to write on the display or hold on a pending list
1462 void CImageAllocator::Free()
1464 ASSERT(m_lAllocated == m_lFree.GetCount());
1465 EXECUTE_ASSERT(GdiFlush());
1466 CImageSample *pSample;
1467 DIBDATA *pDibData;
1469 while (m_lFree.GetCount() != 0) {
1470 pSample = (CImageSample *) m_lFree.RemoveHead();
1471 pDibData = pSample->GetDIBData();
1472 EXECUTE_ASSERT(DeleteObject(pDibData->hBitmap));
1473 EXECUTE_ASSERT(CloseHandle(pDibData->hMapping));
1474 delete pSample;
1477 m_lAllocated = 0;
1481 // Prepare the allocator by checking all the input parameters
1483 STDMETHODIMP CImageAllocator::CheckSizes(ALLOCATOR_PROPERTIES *pRequest)
1485 // Check we have a valid connection
1487 if (m_pMediaType == NULL) {
1488 return VFW_E_NOT_CONNECTED;
1491 // NOTE We always create a DIB section with the source format type which
1492 // may contain a source palette. When we do the BitBlt drawing operation
1493 // the target display device may contain a different palette (we may not
1494 // have the focus) in which case GDI will do after the palette mapping
1496 VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *) m_pMediaType->Format();
1498 // When we call CreateDIBSection it implicitly maps only enough memory
1499 // for the image as defined by thee BITMAPINFOHEADER. If the user asks
1500 // for an image smaller than this then we reject the call, if they ask
1501 // for an image larger than this then we return what they can have
1503 if ((DWORD) pRequest->cbBuffer < pVideoInfo->bmiHeader.biSizeImage) {
1504 return E_INVALIDARG;
1507 // Reject buffer prefixes
1509 if (pRequest->cbPrefix > 0) {
1510 return E_INVALIDARG;
1513 pRequest->cbBuffer = pVideoInfo->bmiHeader.biSizeImage;
1514 return NOERROR;
1518 // Agree the number of media sample buffers and their sizes. The base class
1519 // this allocator is derived from allows samples to be aligned only on byte
1520 // boundaries NOTE the buffers are not allocated until the Commit call
1522 STDMETHODIMP CImageAllocator::SetProperties(
1523 ALLOCATOR_PROPERTIES * pRequest,
1524 ALLOCATOR_PROPERTIES * pActual)
1526 ALLOCATOR_PROPERTIES Adjusted = *pRequest;
1528 // Check the parameters fit with the current connection
1530 HRESULT hr = CheckSizes(&Adjusted);
1531 if (FAILED(hr)) {
1532 return hr;
1534 return CBaseAllocator::SetProperties(&Adjusted, pActual);
1538 // Commit the memory by allocating the agreed number of media samples. For
1539 // each sample we are committed to creating we have a CImageSample object
1540 // that we use to manage it's resources. This is initialised with a DIBDATA
1541 // structure that contains amongst other things the GDI DIBSECTION handle
1542 // We will access the renderer media type during this so we must have locked
1543 // (to prevent the format changing for example). The class overrides Commit
1544 // and Decommit to do this locking (base class Commit in turn calls Alloc)
1546 HRESULT CImageAllocator::Alloc(void)
1548 ASSERT(m_pMediaType);
1549 CImageSample *pSample;
1550 DIBDATA DibData;
1552 // Check the base allocator says it's ok to continue
1554 HRESULT hr = CBaseAllocator::Alloc();
1555 if (FAILED(hr)) {
1556 return hr;
1559 // We create a new memory mapped object although we don't map it into our
1560 // address space because GDI does that in CreateDIBSection. It is possible
1561 // that we run out of resources before creating all the samples in which
1562 // case the available sample list is left with those already created
1564 ASSERT(m_lAllocated == 0);
1565 while (m_lAllocated < m_lCount) {
1567 // Create and initialise a shared memory GDI buffer
1569 HRESULT hr = CreateDIB(m_lSize,DibData);
1570 if (FAILED(hr)) {
1571 return hr;
1574 // Create the sample object and pass it the DIBDATA
1576 pSample = CreateImageSample(DibData.pBase,m_lSize);
1577 if (pSample == NULL) {
1578 EXECUTE_ASSERT(DeleteObject(DibData.hBitmap));
1579 EXECUTE_ASSERT(CloseHandle(DibData.hMapping));
1580 return E_OUTOFMEMORY;
1583 // Add the completed sample to the available list
1585 pSample->SetDIBData(&DibData);
1586 m_lFree.Add(pSample);
1587 m_lAllocated++;
1589 return NOERROR;
1593 // We have a virtual method that allocates the samples so that a derived class
1594 // may override it and allocate more specialised sample objects. So long as it
1595 // derives its samples from CImageSample then all this code will still work ok
1597 CImageSample *CImageAllocator::CreateImageSample(LPBYTE pData,LONG Length)
1599 HRESULT hr = NOERROR;
1600 CImageSample *pSample;
1602 // Allocate the new sample and check the return codes
1604 pSample = new CImageSample((CBaseAllocator *) this, // Base class
1605 NAME("Video sample"), // DEBUG name
1606 (HRESULT *) &hr, // Return code
1607 (LPBYTE) pData, // DIB address
1608 (LONG) Length); // Size of DIB
1610 if (pSample == NULL || FAILED(hr)) {
1611 delete pSample;
1612 return NULL;
1614 return pSample;
1618 // This function allocates a shared memory block for use by the source filter
1619 // generating DIBs for us to render. The memory block is created in shared
1620 // memory so that GDI doesn't have to copy the memory when we do a BitBlt
1622 HRESULT CImageAllocator::CreateDIB(LONG InSize,DIBDATA &DibData)
1624 BITMAPINFO *pbmi; // Format information for pin
1625 BYTE *pBase; // Pointer to the actual image
1626 HANDLE hMapping; // Handle to mapped object
1627 HBITMAP hBitmap; // DIB section bitmap handle
1629 // Create a file mapping object and map into our address space
1631 hMapping = CreateFileMapping(hMEMORY, // Use system page file
1632 NULL, // No security attributes
1633 PAGE_READWRITE, // Full access to memory
1634 (DWORD) 0, // Less than 4Gb in size
1635 InSize, // Size of buffer
1636 NULL); // No name to section
1637 if (hMapping == NULL) {
1638 DWORD Error = GetLastError();
1639 return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error);
1642 // NOTE We always create a DIB section with the source format type which
1643 // may contain a source palette. When we do the BitBlt drawing operation
1644 // the target display device may contain a different palette (we may not
1645 // have the focus) in which case GDI will do after the palette mapping
1647 pbmi = (BITMAPINFO *) HEADER(m_pMediaType->Format());
1648 if (m_pMediaType == NULL) {
1649 DbgBreak("Invalid media type");
1652 hBitmap = CreateDIBSection((HDC) NULL, // NO device context
1653 pbmi, // Format information
1654 DIB_RGB_COLORS, // Use the palette
1655 (VOID **) &pBase, // Pointer to image data
1656 hMapping, // Mapped memory handle
1657 (DWORD) 0); // Offset into memory
1659 if (hBitmap == NULL || pBase == NULL) {
1660 EXECUTE_ASSERT(CloseHandle(hMapping));
1661 DWORD Error = GetLastError();
1662 return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error);
1665 // Initialise the DIB information structure
1667 DibData.hBitmap = hBitmap;
1668 DibData.hMapping = hMapping;
1669 DibData.pBase = pBase;
1670 DibData.PaletteVersion = PALETTE_VERSION;
1671 GetObject(hBitmap,sizeof(DIBSECTION),(VOID *)&DibData.DibSection);
1673 return NOERROR;
1677 // We use the media type during the DIBSECTION creation
1679 void CImageAllocator::NotifyMediaType(CMediaType *pMediaType)
1681 m_pMediaType = pMediaType;
1685 // Overriden to increment the owning object's reference count
1687 STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingAddRef()
1689 return m_pFilter->AddRef();
1693 // Overriden to decrement the owning object's reference count
1695 STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingRelease()
1697 return m_pFilter->Release();
1701 // If you derive a class from CMediaSample that has to transport specialised
1702 // member variables and entry points then there are three alternate solutions
1703 // The first is to create a memory buffer larger than actually required by the
1704 // sample and store your information either at the beginning of it or at the
1705 // end, the former being moderately safer allowing for misbehaving transform
1706 // filters. You then adjust the buffer address when you create the base media
1707 // sample. This has the disadvantage of breaking up the memory allocated to
1708 // the samples into separate blocks. The second solution is to implement a
1709 // class derived from CMediaSample and support additional interface(s) that
1710 // convey your private data. This means defining a custom interface. The final
1711 // alternative is to create a class that inherits from CMediaSample and adds
1712 // the private data structures, when you get an IMediaSample in your Receive()
1713 // call check to see if your allocator is being used, and if it is then cast
1714 // the IMediaSample into one of your objects. Additional checks can be made
1715 // to ensure the sample's this pointer is known to be one of your own objects
1717 CImageSample::CImageSample(CBaseAllocator *pAllocator,
1718 TCHAR *pName,
1719 HRESULT *phr,
1720 LPBYTE pBuffer,
1721 LONG length) :
1722 CMediaSample(pName,pAllocator,phr,pBuffer,length),
1723 m_bInit(FALSE)
1725 ASSERT(pAllocator);
1726 ASSERT(pBuffer);
1730 // Set the shared memory DIB information
1732 void CImageSample::SetDIBData(DIBDATA *pDibData)
1734 ASSERT(pDibData);
1735 m_DibData = *pDibData;
1736 m_bInit = TRUE;
1740 // Retrieve the shared memory DIB data
1742 DIBDATA *CImageSample::GetDIBData()
1744 ASSERT(m_bInit == TRUE);
1745 return &m_DibData;
1749 // This class handles the creation of a palette. It is fairly specialist and
1750 // is intended to simplify palette management for video renderer filters. It
1751 // is for this reason that the constructor requires three other objects with
1752 // which it interacts, namely a base media filter, a base window and a base
1753 // drawing object although the base window or the draw object may be NULL to
1754 // ignore that part of us. We try not to create and install palettes unless
1755 // absolutely necessary as they typically require WM_PALETTECHANGED messages
1756 // to be sent to every window thread in the system which is very expensive
1758 CImagePalette::CImagePalette(CBaseFilter *pBaseFilter,
1759 CBaseWindow *pBaseWindow,
1760 CDrawImage *pDrawImage) :
1761 m_pBaseWindow(pBaseWindow),
1762 m_pFilter(pBaseFilter),
1763 m_pDrawImage(pDrawImage),
1764 m_hPalette(NULL)
1766 ASSERT(m_pFilter);
1770 // Destructor
1772 #ifdef DEBUG
1773 CImagePalette::~CImagePalette()
1775 ASSERT(m_hPalette == NULL);
1777 #endif
1780 // We allow dynamic format changes of the palette but rather than change the
1781 // palette every time we call this to work out whether an update is required.
1782 // If the original type didn't use a palette and the new one does (or vica
1783 // versa) then we return TRUE. If neither formats use a palette we'll return
1784 // FALSE. If both formats use a palette we compare their colours and return
1785 // FALSE if they match. This therefore short circuits palette creation unless
1786 // absolutely necessary since installing palettes is an expensive operation
1788 BOOL CImagePalette::ShouldUpdate(const VIDEOINFOHEADER *pNewInfo,
1789 const VIDEOINFOHEADER *pOldInfo)
1791 // We may not have a current format yet
1793 if (pOldInfo == NULL) {
1794 return TRUE;
1797 // Do both formats not require a palette
1799 if (ContainsPalette(pNewInfo) == FALSE) {
1800 if (ContainsPalette(pOldInfo) == FALSE) {
1801 return FALSE;
1805 // Compare the colours to see if they match
1807 DWORD VideoEntries = pNewInfo->bmiHeader.biClrUsed;
1808 if (ContainsPalette(pNewInfo) == TRUE)
1809 if (ContainsPalette(pOldInfo) == TRUE)
1810 if (pOldInfo->bmiHeader.biClrUsed == VideoEntries)
1811 if (pOldInfo->bmiHeader.biClrUsed > 0)
1812 if (memcmp((PVOID) GetBitmapPalette(pNewInfo),
1813 (PVOID) GetBitmapPalette(pOldInfo),
1814 VideoEntries * sizeof(RGBQUAD)) == 0) {
1816 return FALSE;
1818 return TRUE;
1822 // This is normally called when the input pin type is set to install a palette
1823 // We will typically be called from two different places. The first is when we
1824 // have negotiated a palettised media type after connection, the other is when
1825 // we receive a new type during processing with an updated palette in which
1826 // case we must remove and release the resources held by the current palette
1828 // We can be passed an optional device name if we wish to prepare a palette
1829 // for a specific monitor on a multi monitor system
1831 HRESULT CImagePalette::PreparePalette(const CMediaType *pmtNew,
1832 const CMediaType *pmtOld,
1833 LPSTR szDevice)
1835 const VIDEOINFOHEADER *pNewInfo = (VIDEOINFOHEADER *) pmtNew->Format();
1836 const VIDEOINFOHEADER *pOldInfo = (VIDEOINFOHEADER *) pmtOld->Format();
1837 ASSERT(pNewInfo);
1839 // This is an performance optimisation, when we get a media type we check
1840 // to see if the format requires a palette change. If either we need one
1841 // when previously we didn't or vica versa then this returns TRUE, if we
1842 // previously needed a palette and we do now it compares their colours
1844 if (ShouldUpdate(pNewInfo,pOldInfo) == FALSE) {
1845 NOTE("No update needed");
1846 return S_FALSE;
1849 // We must notify the filter graph that the application may have changed
1850 // the palette although in practice we don't bother checking to see if it
1851 // is really different. If it tries to get the palette either the window
1852 // or renderer lock will ensure it doesn't get in until we are finished
1854 RemovePalette();
1855 m_pFilter->NotifyEvent(EC_PALETTE_CHANGED,0,0);
1857 // Do we need a palette for the new format
1859 if (ContainsPalette(pNewInfo) == FALSE) {
1860 NOTE("New has no palette");
1861 return S_FALSE;
1864 if (m_pBaseWindow) {
1865 m_pBaseWindow->LockPaletteLock();
1868 // If we're changing the palette on the fly then we increment our palette
1869 // cookie which is compared against the cookie also stored in all of our
1870 // DIBSECTION media samples. If they don't match when we come to draw it
1871 // then we know the sample is out of date and we'll update it's palette
1873 NOTE("Making new colour palette");
1874 m_hPalette = MakePalette(pNewInfo, szDevice);
1875 ASSERT(m_hPalette != NULL);
1877 if (m_pBaseWindow) {
1878 m_pBaseWindow->UnlockPaletteLock();
1881 // The window in which the new palette is to be realised may be a NULL
1882 // pointer to signal that no window is in use, if so we don't call it
1883 // Some filters just want to use this object to create/manage palettes
1885 if (m_pBaseWindow) m_pBaseWindow->SetPalette(m_hPalette);
1887 // This is the only time where we need access to the draw object to say
1888 // to it that a new palette will be arriving on a sample real soon. The
1889 // constructor may take a NULL pointer in which case we don't call this
1891 if (m_pDrawImage) m_pDrawImage->IncrementPaletteVersion();
1892 return NOERROR;
1896 // Helper function to copy a palette out of any kind of VIDEOINFO (ie it may
1897 // be YUV or true colour) into a palettised VIDEOINFO. We use this changing
1898 // palettes on DirectDraw samples as a source filter can attach a palette to
1899 // any buffer (eg YUV) and hand it back. We make a new palette out of that
1900 // format and then copy the palette colours into the current connection type
1902 HRESULT CImagePalette::CopyPalette(const CMediaType *pSrc,CMediaType *pDest)
1904 // Reset the destination palette before starting
1906 VIDEOINFOHEADER *pDestInfo = (VIDEOINFOHEADER *) pDest->Format();
1907 pDestInfo->bmiHeader.biClrUsed = 0;
1908 pDestInfo->bmiHeader.biClrImportant = 0;
1910 // Does the destination have a palette
1912 if (PALETTISED(pDestInfo) == FALSE) {
1913 NOTE("No destination palette");
1914 return S_FALSE;
1917 // Does the source contain a palette
1919 const VIDEOINFOHEADER *pSrcInfo = (VIDEOINFOHEADER *) pSrc->Format();
1920 if (ContainsPalette(pSrcInfo) == FALSE) {
1921 NOTE("No source palette");
1922 return S_FALSE;
1925 // The number of colours may be zero filled
1927 DWORD PaletteEntries = pSrcInfo->bmiHeader.biClrUsed;
1928 if (PaletteEntries == 0) {
1929 DWORD Maximum = (1 << pSrcInfo->bmiHeader.biBitCount);
1930 NOTE1("Setting maximum colours (%d)",Maximum);
1931 PaletteEntries = Maximum;
1934 // Make sure the destination has enough room for the palette
1936 ASSERT(pSrcInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);
1937 ASSERT(pSrcInfo->bmiHeader.biClrImportant <= PaletteEntries);
1938 ASSERT(COLORS(pDestInfo) == GetBitmapPalette(pDestInfo));
1939 pDestInfo->bmiHeader.biClrUsed = PaletteEntries;
1940 pDestInfo->bmiHeader.biClrImportant = pSrcInfo->bmiHeader.biClrImportant;
1941 ULONG BitmapSize = GetBitmapFormatSize(HEADER(pSrcInfo));
1943 if (pDest->FormatLength() < BitmapSize) {
1944 NOTE("Reallocating destination");
1945 pDest->ReallocFormatBuffer(BitmapSize);
1948 // Now copy the palette colours across
1950 CopyMemory((PVOID) COLORS(pDestInfo),
1951 (PVOID) GetBitmapPalette(pSrcInfo),
1952 PaletteEntries * sizeof(RGBQUAD));
1954 return NOERROR;
1958 // This is normally called when the palette is changed (typically during a
1959 // dynamic format change) to remove any palette we previously installed. We
1960 // replace it (if necessary) in the video window with a standard VGA palette
1961 // that should always be available even if this is a true colour display
1963 HRESULT CImagePalette::RemovePalette()
1965 if (m_pBaseWindow) {
1966 m_pBaseWindow->LockPaletteLock();
1969 // Do we have a palette to remove
1971 if (m_hPalette != NULL) {
1973 if (m_pBaseWindow) {
1974 // Make sure that the window's palette handle matches
1975 // our palette handle.
1976 ASSERT(m_hPalette == m_pBaseWindow->GetPalette());
1978 m_pBaseWindow->UnsetPalette();
1981 EXECUTE_ASSERT(DeleteObject(m_hPalette));
1982 m_hPalette = NULL;
1985 if (m_pBaseWindow) {
1986 m_pBaseWindow->UnlockPaletteLock();
1989 return NOERROR;
1993 // Called to create a palette for the object, the data structure used by GDI
1994 // to describe a palette is a LOGPALETTE, this includes a variable number of
1995 // PALETTEENTRY fields which are the colours, we have to convert the RGBQUAD
1996 // colour fields we are handed in a BITMAPINFO from the media type into these
1997 // This handles extraction of palettes from true colour and YUV media formats
1999 // We can be passed an optional device name if we wish to prepare a palette
2000 // for a specific monitor on a multi monitor system
2002 HPALETTE CImagePalette::MakePalette(const VIDEOINFOHEADER *pVideoInfo, LPSTR szDevice)
2004 ASSERT(ContainsPalette(pVideoInfo) == TRUE);
2005 ASSERT(pVideoInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);
2006 BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo);
2008 const RGBQUAD *pColours; // Pointer to the palette
2009 LOGPALETTE *lp; // Used to create a palette
2010 HPALETTE hPalette; // Logical palette object
2012 lp = (LOGPALETTE *) new BYTE[sizeof(LOGPALETTE) + SIZE_PALETTE];
2013 if (lp == NULL) {
2014 return NULL;
2017 // Unfortunately for some hare brained reason a GDI palette entry (a
2018 // PALETTEENTRY structure) is different to a palette entry from a DIB
2019 // format (a RGBQUAD structure) so we have to do the field conversion
2020 // The VIDEOINFO containing the palette may be a true colour type so
2021 // we use GetBitmapPalette to skip over any bit fields if they exist
2023 lp->palVersion = PALVERSION;
2024 lp->palNumEntries = (USHORT) pHeader->biClrUsed;
2025 if (lp->palNumEntries == 0) lp->palNumEntries = (1 << pHeader->biBitCount);
2026 pColours = GetBitmapPalette(pVideoInfo);
2028 for (DWORD dwCount = 0;dwCount < lp->palNumEntries;dwCount++) {
2029 lp->palPalEntry[dwCount].peRed = pColours[dwCount].rgbRed;
2030 lp->palPalEntry[dwCount].peGreen = pColours[dwCount].rgbGreen;
2031 lp->palPalEntry[dwCount].peBlue = pColours[dwCount].rgbBlue;
2032 lp->palPalEntry[dwCount].peFlags = 0;
2035 MakeIdentityPalette(lp->palPalEntry, lp->palNumEntries, szDevice);
2037 // Create a logical palette
2039 hPalette = CreatePalette(lp);
2040 ASSERT(hPalette != NULL);
2041 delete[] lp;
2042 return hPalette;
2046 // GDI does a fair job of compressing the palette entries you give it, so for
2047 // example if you have five entries with an RGB colour (0,0,0) it will remove
2048 // all but one of them. When you subsequently draw an image it will map from
2049 // your logical palette to the compressed device palette. This function looks
2050 // to see if it is trying to be an identity palette and if so sets the flags
2051 // field in the PALETTEENTRYs so they remain expanded to boost performance
2053 // We can be passed an optional device name if we wish to prepare a palette
2054 // for a specific monitor on a multi monitor system
2056 HRESULT CImagePalette::MakeIdentityPalette(PALETTEENTRY *pEntry,INT iColours, LPSTR szDevice)
2058 PALETTEENTRY SystemEntries[10]; // System palette entries
2059 BOOL bIdentityPalette = TRUE; // Is an identity palette
2060 ASSERT(iColours <= iPALETTE_COLORS); // Should have a palette
2061 const int PalLoCount = 10; // First ten reserved colours
2062 const int PalHiStart = 246; // Last VGA palette entries
2064 // Does this have the full colour range
2066 if (iColours < 10) {
2067 return S_FALSE;
2070 // Apparently some displays have odd numbers of system colours
2072 // Get a DC on the right monitor - it's ugly, but this is the way you have
2073 // to do it
2074 HDC hdc;
2075 if (szDevice == NULL || lstrcmpiA(szDevice, "DISPLAY") == 0)
2076 hdc = CreateDCA("DISPLAY", NULL, NULL, NULL);
2077 else
2078 hdc = CreateDCA(NULL, szDevice, NULL, NULL);
2079 if (NULL == hdc) {
2080 return E_OUTOFMEMORY;
2082 INT Reserved = GetDeviceCaps(hdc,NUMRESERVED);
2083 if (Reserved != 20) {
2084 DeleteDC(hdc);
2085 return S_FALSE;
2088 // Compare our palette against the first ten system entries. The reason I
2089 // don't do a memory compare between our two arrays of colours is because
2090 // I am not sure what will be in the flags fields for the system entries
2092 UINT Result = GetSystemPaletteEntries(hdc,0,PalLoCount,SystemEntries);
2093 for (UINT Count = 0;Count < Result;Count++) {
2094 if (SystemEntries[Count].peRed != pEntry[Count].peRed ||
2095 SystemEntries[Count].peGreen != pEntry[Count].peGreen ||
2096 SystemEntries[Count].peBlue != pEntry[Count].peBlue) {
2097 bIdentityPalette = FALSE;
2101 // And likewise compare against the last ten entries
2103 Result = GetSystemPaletteEntries(hdc,PalHiStart,PalLoCount,SystemEntries);
2104 for (UINT Count = 0;Count < Result;Count++) {
2105 if (INT(Count) + PalHiStart < iColours) {
2106 if (SystemEntries[Count].peRed != pEntry[PalHiStart + Count].peRed ||
2107 SystemEntries[Count].peGreen != pEntry[PalHiStart + Count].peGreen ||
2108 SystemEntries[Count].peBlue != pEntry[PalHiStart + Count].peBlue) {
2109 bIdentityPalette = FALSE;
2114 // If not an identity palette then return S_FALSE
2116 DeleteDC(hdc);
2117 if (bIdentityPalette == FALSE) {
2118 return S_FALSE;
2121 // Set the non VGA entries so that GDI doesn't map them
2123 for (UINT Count = PalLoCount;INT(Count) < min(PalHiStart,iColours);Count++) {
2124 pEntry[Count].peFlags = PC_NOCOLLAPSE;
2126 return NOERROR;
2130 // Constructor initialises the VIDEOINFO we keep storing the current display
2131 // format. The format can be changed at any time, to reset the format held
2132 // by us call the RefreshDisplayType directly (it's a public method). Since
2133 // more than one thread will typically call us (ie window threads resetting
2134 // the type and source threads in the type checking methods) we have a lock
2136 CImageDisplay::CImageDisplay()
2138 RefreshDisplayType(NULL);
2143 // This initialises the format we hold which contains the display device type
2144 // We do a conversion on the display device type in here so that when we start
2145 // type checking input formats we can assume that certain fields have been set
2146 // correctly, an example is when we make the 16 bit mask fields explicit. This
2147 // is normally called when we receive WM_DEVMODECHANGED device change messages
2149 // The optional szDeviceName parameter tells us which monitor we are interested
2150 // in for a multi monitor system
2152 HRESULT CImageDisplay::RefreshDisplayType(LPSTR szDeviceName)
2154 CAutoLock cDisplayLock(this);
2156 // Set the preferred format type
2158 ZeroMemory((PVOID)&m_Display,sizeof(VIDEOINFOHEADER)+sizeof(TRUECOLORINFO));
2159 m_Display.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
2160 m_Display.bmiHeader.biBitCount = FALSE;
2162 // Get the bit depth of a device compatible bitmap
2164 // get caps of whichever monitor they are interested in (multi monitor)
2165 HDC hdcDisplay;
2166 // it's ugly, but this is the way you have to do it
2167 if (szDeviceName == NULL || lstrcmpiA(szDeviceName, "DISPLAY") == 0)
2168 hdcDisplay = CreateDCA("DISPLAY", NULL, NULL, NULL);
2169 else
2170 hdcDisplay = CreateDCA(NULL, szDeviceName, NULL, NULL);
2171 if (hdcDisplay == NULL) {
2172 ASSERT(FALSE);
2173 DbgLog((LOG_ERROR,1,TEXT("ACK! Can't get a DC for %hs"),
2174 szDeviceName ? szDeviceName : "<NULL>"));
2175 return E_FAIL;
2176 } else {
2177 DbgLog((LOG_TRACE,3,TEXT("Created a DC for %s"),
2178 szDeviceName ? szDeviceName : "<NULL>"));
2180 HBITMAP hbm = CreateCompatibleBitmap(hdcDisplay,1,1);
2181 if ( hbm )
2183 GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);
2185 // This call will get the colour table or the proper bitfields
2186 GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);
2187 DeleteObject(hbm);
2189 DeleteDC(hdcDisplay);
2191 // Complete the display type initialisation
2193 ASSERT(CheckHeaderValidity(&m_Display));
2194 UpdateFormat(&m_Display);
2195 DbgLog((LOG_TRACE,3,TEXT("New DISPLAY bit depth =%d"),
2196 m_Display.bmiHeader.biBitCount));
2197 return NOERROR;
2201 // We assume throughout this code that any bitfields masks are allowed no
2202 // more than eight bits to store a colour component. This checks that the
2203 // bit count assumption is enforced and also makes sure that all the bits
2204 // set are contiguous. We return a boolean TRUE if the field checks out ok
2206 BOOL CImageDisplay::CheckBitFields(const VIDEOINFO *pInput)
2208 DWORD *pBitFields = (DWORD *) BITMASKS(pInput);
2210 for (INT iColour = iRED;iColour <= iBLUE;iColour++) {
2212 // First of all work out how many bits are set
2214 DWORD SetBits = CountSetBits(pBitFields[iColour]);
2215 if (SetBits > iMAXBITS || SetBits == 0) {
2216 NOTE1("Bit fields for component %d invalid",iColour);
2217 return FALSE;
2220 // Next work out the number of zero bits prefix
2221 DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);
2223 // This is going to see if all the bits set are contiguous (as they
2224 // should be). We know how much to shift them right by from the
2225 // count of prefix bits. The number of bits set defines a mask, we
2226 // invert this (ones complement) and AND it with the shifted bit
2227 // fields. If the result is NON zero then there are bit(s) sticking
2228 // out the left hand end which means they are not contiguous
2230 DWORD TestField = pBitFields[iColour] >> PrefixBits;
2231 DWORD Mask = ULONG_MAX << SetBits;
2232 if (TestField & Mask) {
2233 NOTE1("Bit fields for component %d not contiguous",iColour);
2234 return FALSE;
2237 return TRUE;
2241 // This counts the number of bits set in the input field
2243 DWORD CImageDisplay::CountSetBits(DWORD Field)
2245 // This is a relatively well known bit counting algorithm
2247 DWORD Count = 0;
2248 DWORD init = Field;
2250 // Until the input is exhausted, count the number of bits
2252 while (init) {
2253 init = init & (init - 1); // Turn off the bottommost bit
2254 Count++;
2256 return Count;
2260 // This counts the number of zero bits upto the first one set NOTE the input
2261 // field should have been previously checked to ensure there is at least one
2262 // set although if we don't find one set we return the impossible value 32
2264 DWORD CImageDisplay::CountPrefixBits(DWORD Field)
2266 DWORD Mask = 1;
2267 DWORD Count = 0;
2269 while (TRUE) {
2270 if (Field & Mask) {
2271 return Count;
2273 Count++;
2275 ASSERT(Mask != 0x80000000);
2276 if (Mask == 0x80000000) {
2277 return Count;
2279 Mask <<= 1;
2284 // This is called to check the BITMAPINFOHEADER for the input type. There are
2285 // many implicit dependancies between the fields in a header structure which
2286 // if we validate now make for easier manipulation in subsequent handling. We
2287 // also check that the BITMAPINFOHEADER matches it's specification such that
2288 // fields likes the number of planes is one, that it's structure size is set
2289 // correctly and that the bitmap dimensions have not been set as negative
2291 BOOL CImageDisplay::CheckHeaderValidity(const VIDEOINFO *pInput)
2293 // Check the bitmap width and height are not negative.
2295 if (pInput->bmiHeader.biWidth <= 0 ||
2296 pInput->bmiHeader.biHeight <= 0) {
2297 NOTE("Invalid bitmap dimensions");
2298 return FALSE;
2301 // Check the compression is either BI_RGB or BI_BITFIELDS
2303 if (pInput->bmiHeader.biCompression != BI_RGB) {
2304 if (pInput->bmiHeader.biCompression != BI_BITFIELDS) {
2305 NOTE("Invalid compression format");
2306 return FALSE;
2310 // If BI_BITFIELDS compression format check the colour depth
2312 if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {
2313 if (pInput->bmiHeader.biBitCount != 16) {
2314 if (pInput->bmiHeader.biBitCount != 32) {
2315 NOTE("BI_BITFIELDS not 16/32 bit depth");
2316 return FALSE;
2321 // Check the assumptions about the layout of the bit fields
2323 if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {
2324 if (CheckBitFields(pInput) == FALSE) {
2325 NOTE("Bit fields are not valid");
2326 return FALSE;
2330 // Are the number of planes equal to one
2332 if (pInput->bmiHeader.biPlanes != 1) {
2333 NOTE("Number of planes not one");
2334 return FALSE;
2337 // Check the image size is consistent (it can be zero)
2339 if (pInput->bmiHeader.biSizeImage != GetBitmapSize(&pInput->bmiHeader)) {
2340 if (pInput->bmiHeader.biSizeImage) {
2341 NOTE("Image size incorrectly set");
2342 return FALSE;
2346 // Check the size of the structure
2348 if (pInput->bmiHeader.biSize != sizeof(BITMAPINFOHEADER)) {
2349 NOTE("Size of BITMAPINFOHEADER wrong");
2350 return FALSE;
2352 return CheckPaletteHeader(pInput);
2356 // This runs a few simple tests against the palette fields in the input to
2357 // see if it looks vaguely correct. The tests look at the number of palette
2358 // colours present, the number considered important and the biCompression
2359 // field which should always be BI_RGB as no other formats are meaningful
2361 BOOL CImageDisplay::CheckPaletteHeader(const VIDEOINFO *pInput)
2363 // The checks here are for palettised videos only
2365 if (PALETTISED(pInput) == FALSE) {
2366 if (pInput->bmiHeader.biClrUsed) {
2367 NOTE("Invalid palette entries");
2368 return FALSE;
2370 return TRUE;
2373 // Compression type of BI_BITFIELDS is meaningless for palette video
2375 if (pInput->bmiHeader.biCompression != BI_RGB) {
2376 NOTE("Palettised video must be BI_RGB");
2377 return FALSE;
2380 // Check the number of palette colours is correct
2382 if (pInput->bmiHeader.biClrUsed > PALETTE_ENTRIES(pInput)) {
2383 NOTE("Too many colours in palette");
2384 return FALSE;
2387 // The number of important colours shouldn't exceed the number used
2389 if (pInput->bmiHeader.biClrImportant > pInput->bmiHeader.biClrUsed) {
2390 NOTE("Too many important colours");
2391 return FALSE;
2393 return TRUE;
2397 // Return the format of the video display
2399 const VIDEOINFO *CImageDisplay::GetDisplayFormat()
2401 return &m_Display;
2405 // Return TRUE if the display uses a palette
2407 BOOL CImageDisplay::IsPalettised()
2409 return PALETTISED(&m_Display);
2413 // Return the bit depth of the current display setting
2415 WORD CImageDisplay::GetDisplayDepth()
2417 return m_Display.bmiHeader.biBitCount;
2421 // Initialise the optional fields in a VIDEOINFO. These are mainly to do with
2422 // the source and destination rectangles and palette information such as the
2423 // number of colours present. It simplifies our code just a little if we don't
2424 // have to keep checking for all the different valid permutations in a header
2425 // every time we want to do anything with it (an example would be creating a
2426 // palette). We set the base class media type before calling this function so
2427 // that the media types between the pins match after a connection is made
2429 HRESULT CImageDisplay::UpdateFormat(VIDEOINFO *pVideoInfo)
2431 ASSERT(pVideoInfo);
2433 BITMAPINFOHEADER *pbmi = HEADER(pVideoInfo);
2434 SetRectEmpty(&pVideoInfo->rcSource);
2435 SetRectEmpty(&pVideoInfo->rcTarget);
2437 // Set the number of colours explicitly
2439 if (PALETTISED(pVideoInfo)) {
2440 if (pVideoInfo->bmiHeader.biClrUsed == 0) {
2441 pVideoInfo->bmiHeader.biClrUsed = PALETTE_ENTRIES(pVideoInfo);
2445 // The number of important colours shouldn't exceed the number used, on
2446 // some displays the number of important colours is not initialised when
2447 // retrieving the display type so we set the colours used correctly
2449 if (pVideoInfo->bmiHeader.biClrImportant > pVideoInfo->bmiHeader.biClrUsed) {
2450 pVideoInfo->bmiHeader.biClrImportant = PALETTE_ENTRIES(pVideoInfo);
2453 // Change the image size field to be explicit
2455 if (pVideoInfo->bmiHeader.biSizeImage == 0) {
2456 pVideoInfo->bmiHeader.biSizeImage = GetBitmapSize(&pVideoInfo->bmiHeader);
2458 return NOERROR;
2462 // Lots of video rendering filters want code to check proposed formats are ok
2463 // This checks the VIDEOINFO we are passed as a media type. If the media type
2464 // is a valid media type then we return NOERROR otherwise E_INVALIDARG. Note
2465 // however we only accept formats that can be easily displayed in the display
2466 // so if we are on a 16 bit device we will not accept 24 bit images. The one
2467 // complexity is that most displays draw 8 bit palettised images efficiently
2468 // Also if the input format is less colour bits per pixel then we also accept
2470 HRESULT CImageDisplay::CheckVideoType(const VIDEOINFO *pInput)
2472 // First of all check the VIDEOINFOHEADER looks correct
2474 if (CheckHeaderValidity(pInput) == FALSE) {
2475 return E_INVALIDARG;
2478 // Virtually all devices support palettised images efficiently
2480 if (m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount) {
2481 if (PALETTISED(pInput) == TRUE) {
2482 ASSERT(PALETTISED(&m_Display) == TRUE);
2483 NOTE("(Video) Type connection ACCEPTED");
2484 return NOERROR;
2489 // Is the display depth greater than the input format
2491 if (m_Display.bmiHeader.biBitCount > pInput->bmiHeader.biBitCount) {
2492 NOTE("(Video) Mismatch agreed");
2493 return NOERROR;
2496 // Is the display depth less than the input format
2498 if (m_Display.bmiHeader.biBitCount < pInput->bmiHeader.biBitCount) {
2499 NOTE("(Video) Format mismatch");
2500 return E_INVALIDARG;
2504 // Both input and display formats are either BI_RGB or BI_BITFIELDS
2506 ASSERT(m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount);
2507 ASSERT(PALETTISED(pInput) == FALSE);
2508 ASSERT(PALETTISED(&m_Display) == FALSE);
2510 // BI_RGB 16 bit representation is implicitly RGB555, and likewise BI_RGB
2511 // 24 bit representation is RGB888. So we initialise a pointer to the bit
2512 // fields they really mean and check against the display device format
2513 // This is only going to be called when both formats are equal bits pixel
2515 const DWORD *pInputMask = GetBitMasks(pInput);
2516 const DWORD *pDisplayMask = GetBitMasks((VIDEOINFO *)&m_Display);
2518 if (pInputMask[iRED] != pDisplayMask[iRED] ||
2519 pInputMask[iGREEN] != pDisplayMask[iGREEN] ||
2520 pInputMask[iBLUE] != pDisplayMask[iBLUE]) {
2522 NOTE("(Video) Bit field mismatch");
2523 return E_INVALIDARG;
2526 NOTE("(Video) Type connection ACCEPTED");
2527 return NOERROR;
2531 // Return the bit masks for the true colour VIDEOINFO provided
2533 const DWORD *CImageDisplay::GetBitMasks(const VIDEOINFO *pVideoInfo)
2535 static const DWORD FailMasks[] = {0,0,0};
2537 if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {
2538 return BITMASKS(pVideoInfo);
2541 ASSERT(pVideoInfo->bmiHeader.biCompression == BI_RGB);
2543 switch (pVideoInfo->bmiHeader.biBitCount) {
2544 case 16: return bits555;
2545 case 24: return bits888;
2546 case 32: return bits888;
2547 default: return FailMasks;
2552 // Check to see if we can support media type pmtIn as proposed by the output
2553 // pin - We first check that the major media type is video and also identify
2554 // the media sub type. Then we thoroughly check the VIDEOINFO type provided
2555 // As well as the contained VIDEOINFO being correct the major type must be
2556 // video, the subtype a recognised video format and the type GUID correct
2558 HRESULT CImageDisplay::CheckMediaType(const CMediaType *pmtIn)
2560 // Does this have a VIDEOINFOHEADER format block
2562 const GUID *pFormatType = pmtIn->FormatType();
2563 if (*pFormatType != FORMAT_VideoInfo) {
2564 NOTE("Format GUID not a VIDEOINFOHEADER");
2565 return E_INVALIDARG;
2567 ASSERT(pmtIn->Format());
2569 // Check the format looks reasonably ok
2571 ULONG Length = pmtIn->FormatLength();
2572 if (Length < SIZE_VIDEOHEADER) {
2573 NOTE("Format smaller than a VIDEOHEADER");
2574 return E_FAIL;
2577 VIDEOINFO *pInput = (VIDEOINFO *) pmtIn->Format();
2579 // Check the major type is MEDIATYPE_Video
2581 const GUID *pMajorType = pmtIn->Type();
2582 if (*pMajorType != MEDIATYPE_Video) {
2583 NOTE("Major type not MEDIATYPE_Video");
2584 return E_INVALIDARG;
2587 // Check we can identify the media subtype
2589 const GUID *pSubType = pmtIn->Subtype();
2590 if (GetBitCount(pSubType) == USHRT_MAX) {
2591 NOTE("Invalid video media subtype");
2592 return E_INVALIDARG;
2594 return CheckVideoType(pInput);
2598 // Given a video format described by a VIDEOINFO structure we return the mask
2599 // that is used to obtain the range of acceptable colours for this type, for
2600 // example, the mask for a 24 bit true colour format is 0xFF in all cases. A
2601 // 16 bit 5:6:5 display format uses 0xF8, 0xFC and 0xF8, therefore given any
2602 // RGB triplets we can AND them with these fields to find one that is valid
2604 BOOL CImageDisplay::GetColourMask(DWORD *pMaskRed,
2605 DWORD *pMaskGreen,
2606 DWORD *pMaskBlue)
2608 CAutoLock cDisplayLock(this);
2609 *pMaskRed = 0xFF;
2610 *pMaskGreen = 0xFF;
2611 *pMaskBlue = 0xFF;
2613 // If this format is palettised then it doesn't have bit fields
2615 if (m_Display.bmiHeader.biBitCount < 16) {
2616 return FALSE;
2619 // If this is a 24 bit true colour display then it can handle all the
2620 // possible colour component ranges described by a byte. It is never
2621 // allowed for a 24 bit colour depth image to have BI_BITFIELDS set
2623 if (m_Display.bmiHeader.biBitCount == 24) {
2624 ASSERT(m_Display.bmiHeader.biCompression == BI_RGB);
2625 return TRUE;
2628 // Calculate the mask based on the format's bit fields
2630 const DWORD *pBitFields = (DWORD *) GetBitMasks((VIDEOINFO *)&m_Display);
2631 DWORD *pOutputMask[] = { pMaskRed, pMaskGreen, pMaskBlue };
2633 // We know from earlier testing that there are no more than iMAXBITS
2634 // bits set in the mask and that they are all contiguous. All that
2635 // therefore remains is to shift them into the correct position
2637 for (INT iColour = iRED;iColour <= iBLUE;iColour++) {
2639 // This works out how many bits there are and where they live
2641 DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);
2642 DWORD SetBits = CountSetBits(pBitFields[iColour]);
2644 // The first shift moves the bit field so that it is right justified
2645 // in the DWORD, after which we then shift it back left which then
2646 // puts the leading bit in the bytes most significant bit position
2648 *(pOutputMask[iColour]) = pBitFields[iColour] >> PrefixBits;
2649 *(pOutputMask[iColour]) <<= (iMAXBITS - SetBits);
2651 return TRUE;
2655 /* Helper to convert to VIDEOINFOHEADER2
2657 STDAPI ConvertVideoInfoToVideoInfo2(AM_MEDIA_TYPE *pmt)
2659 ASSERT(pmt->formattype == FORMAT_VideoInfo);
2660 VIDEOINFO *pVideoInfo = (VIDEOINFO *)pmt->pbFormat;
2661 PVOID pvNew = CoTaskMemAlloc(pmt->cbFormat + sizeof(VIDEOINFOHEADER2) -
2662 sizeof(VIDEOINFOHEADER));
2663 if (pvNew == NULL) {
2664 return E_OUTOFMEMORY;
2666 CopyMemory(pvNew, pmt->pbFormat, FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader));
2667 ZeroMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader),
2668 sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER));
2669 CopyMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER2, bmiHeader),
2670 pmt->pbFormat + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader),
2671 pmt->cbFormat - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader));
2672 VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pvNew;
2673 pVideoInfo2->dwPictAspectRatioX = (DWORD)pVideoInfo2->bmiHeader.biWidth;
2674 pVideoInfo2->dwPictAspectRatioY = (DWORD)pVideoInfo2->bmiHeader.biHeight;
2675 pmt->formattype = FORMAT_VideoInfo2;
2676 CoTaskMemFree(pmt->pbFormat);
2677 pmt->pbFormat = (PBYTE)pvNew;
2678 pmt->cbFormat += sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER);
2679 return S_OK;