1 //------------------------------------------------------------------------------
4 // Desc: DirectShow base classes - implements generic window handler class.
6 // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
7 //------------------------------------------------------------------------------
14 static UINT MsgDestroy
;
18 CBaseWindow::CBaseWindow(BOOL bDoGetDC
, bool bDoPostToDestroy
) :
27 m_ShowStageMessage(0),
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
,
60 if (m_pClassName
== NULL
) {
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
96 CBaseWindow::~CBaseWindow()
98 ASSERT(m_hwnd
== NULL
);
99 ASSERT(m_hdc
== NULL
);
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
) {
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
);
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.
140 // We need to free this DC though because USER32 does not know
141 // anything about it.
145 EXECUTE_ASSERT(DeleteDC(m_MemoryDC
));
149 // Reset the window variables
154 const HWND hwnd
= m_hwnd
;
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()));
177 // Reset our state so we can be prepared again
182 m_WindowStylesEx
= 0;
183 m_ShowStageMessage
= 0;
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
) {
203 m_bActivated
= FALSE
;
204 ShowWindow(m_hwnd
,SW_HIDE
);
209 HRESULT
CBaseWindow::CompleteConnect()
211 m_bActivated
= FALSE
;
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
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
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
)) {
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
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
);
310 m_hPalette
= hPalette
;
316 HRESULT
CBaseWindow::SetPalette()
319 SendMessage(m_hwnd
, m_RealizePalette
, 0, 0);
322 // Just select the palette
326 CAutoLock
cPaletteLock(&m_PaletteLock
);
327 SelectPalette(m_hdc
,m_hPalette
,m_bBackground
);
328 SelectPalette(m_MemoryDC
,m_hPalette
,m_bBackground
);
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
);
345 SelectPalette(GetWindowHDC(), hPalette
, TRUE
);
346 SelectPalette(GetMemoryHDC(), hPalette
, TRUE
);
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
) {
375 // Realize the palette on the window thread
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
442 SetLastError(0); // because of the way SetWindowLong works
444 LONG_PTR rc
= SetWindowLongPtr(hwnd
, (DWORD
) 0, (LONG_PTR
) pBaseWindow
);
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.
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
));
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
)
481 // This function handles the WM_CLOSE message
483 BOOL
CBaseWindow::OnClose()
485 ShowWindow(m_hwnd
,SW_HIDE
);
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
);
506 // Release the window resources
508 EXECUTE_ASSERT(GdiFlush());
512 EXECUTE_ASSERT(ReleaseDC(m_hwnd
,m_hdc
));
518 EXECUTE_ASSERT(DeleteDC(m_MemoryDC
));
522 // Reset the window variables
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
));
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
));
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
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
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
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
));
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
))
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
);
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));
686 if (uMsg
== m_RealizePalette
) {
687 ASSERT(m_hwnd
== hwnd
);
688 return OnPaletteChange(m_hwnd
,WM_QUERYNEWPALETTE
);
693 // Repaint the window if the system colours change
695 case WM_SYSCOLORCHANGE
:
697 InvalidateRect(hwnd
,NULL
,FALSE
);
700 // Somebody has changed the palette
701 case WM_PALETTECHANGED
:
703 OnPaletteChange((HWND
)wParam
,uMsg
);
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
719 if (IsWindowVisible(m_hwnd
)) {
720 PostMessage(m_hwnd
,WM_PAINT
,0,0);
724 // Store the width and height as useful base class members
728 OnSize(LOWORD(lParam
), HIWORD(lParam
));
731 // Intercept the WM_CLOSE messages to hide the window
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
) {
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!")));
770 // Avoid recursion with multiple graphs in the same app
774 DoRealisePalette(Message
!= WM_QUERYNEWPALETTE
);
776 m_bRealizing
= FALSE
;
779 // Should we redraw the window with the new palette
780 if (Message
== WM_PALETTECHANGED
) {
781 InvalidateRect(m_hwnd
,NULL
,FALSE
);
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
};
808 // Return the current window width
810 LONG
CBaseWindow::GetWindowWidth()
818 // Return the current window height
820 LONG
CBaseWindow::GetWindowHeight()
828 // Return the window handle
830 HWND
CBaseWindow::GetWindowHWND()
838 // Return the window drawing device context
840 HDC
CBaseWindow::GetWindowHDC()
848 // Return the offscreen window drawing device context
850 HDC
CBaseWindow::GetMemoryHDC()
859 HPALETTE
CBaseWindow::GetPalette()
861 // The palette lock should always be held when accessing
863 ASSERT(CritCheckIn(&m_PaletteLock
));
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
);
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
),
911 m_bUsingImageAllocator(FALSE
)
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
)
931 // Only allow the "annoying" time messages if the users has turned the
934 BOOL bAccept
= DbgCheckModuleLevel(LOG_TRACE
, 5);
935 if (bAccept
== FALSE
) {
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,
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
)
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
);
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
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
1075 // Stretch the image when copying to the window
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
1096 DisplaySampleTimes(pMediaSample
);
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());
1120 // Get the image data buffer
1122 HRESULT hr
= pMediaSample
->GetPointer(&pImage
);
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
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
1164 // Stretch the image when copying to the window
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
1191 DisplaySampleTimes(pMediaSample
);
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
)
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());
1221 // This is a DIBSECTION buffer
1223 FastRender(pMediaSample
);
1224 EXECUTE_ASSERT(GdiFlush());
1230 BOOL
CDrawImage::DrawVideoImageHere(
1232 IMediaSample
*pMediaSample
,
1237 ASSERT(m_pMediaType
);
1238 BITMAPINFOHEADER
*pbmi
= HEADER(m_pMediaType
->Format());
1241 // Get the image data buffer
1243 HRESULT hr
= pMediaSample
->GetPointer(&pImage
);
1252 SourceRect
= *lprcSrc
;
1254 else SourceRect
= ScaleSourceRect(&m_SourceRect
);
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
,
1274 TargetRect
.right
- TargetRect
.left
,
1275 TargetRect
.bottom
- TargetRect
.top
,
1278 SourceRect
.right
- SourceRect
.left
,
1279 SourceRect
.bottom
- SourceRect
.top
,
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
;
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
;
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
;
1358 if (SourceWidth
== SinkWidth
) {
1359 if (SourceHeight
== SinkHeight
) {
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()
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
,
1439 CBaseAllocator(pName
,NULL
,phr
,TRUE
,TRUE
),
1447 // Check our DIB buffers have been released
1450 CImageAllocator::~CImageAllocator()
1452 ASSERT(m_bCommitted
== FALSE
);
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
;
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
));
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
;
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
);
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
;
1552 // Check the base allocator says it's ok to continue
1554 HRESULT hr
= CBaseAllocator::Alloc();
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
);
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
);
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
)) {
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
);
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
,
1722 CMediaSample(pName
,pAllocator
,phr
,pBuffer
,length
),
1730 // Set the shared memory DIB information
1732 void CImageSample::SetDIBData(DIBDATA
*pDibData
)
1735 m_DibData
= *pDibData
;
1740 // Retrieve the shared memory DIB data
1742 DIBDATA
*CImageSample::GetDIBData()
1744 ASSERT(m_bInit
== TRUE
);
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
),
1773 CImagePalette::~CImagePalette()
1775 ASSERT(m_hPalette
== NULL
);
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
) {
1797 // Do both formats not require a palette
1799 if (ContainsPalette(pNewInfo
) == FALSE
) {
1800 if (ContainsPalette(pOldInfo
) == 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) {
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
,
1835 const VIDEOINFOHEADER
*pNewInfo
= (VIDEOINFOHEADER
*) pmtNew
->Format();
1836 const VIDEOINFOHEADER
*pOldInfo
= (VIDEOINFOHEADER
*) pmtOld
->Format();
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");
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
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");
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();
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");
1917 // Does the source contain a palette
1919 const VIDEOINFOHEADER
*pSrcInfo
= (VIDEOINFOHEADER
*) pSrc
->Format();
1920 if (ContainsPalette(pSrcInfo
) == FALSE
) {
1921 NOTE("No source palette");
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
));
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
));
1985 if (m_pBaseWindow
) {
1986 m_pBaseWindow
->UnlockPaletteLock();
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
];
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
);
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) {
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
2075 if (szDevice
== NULL
|| lstrcmpiA(szDevice
, "DISPLAY") == 0)
2076 hdc
= CreateDCA("DISPLAY", NULL
, NULL
, NULL
);
2078 hdc
= CreateDCA(NULL
, szDevice
, NULL
, NULL
);
2080 return E_OUTOFMEMORY
;
2082 INT Reserved
= GetDeviceCaps(hdc
,NUMRESERVED
);
2083 if (Reserved
!= 20) {
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
2117 if (bIdentityPalette
== 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
;
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)
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
);
2170 hdcDisplay
= CreateDCA(NULL
, szDeviceName
, NULL
, NULL
);
2171 if (hdcDisplay
== NULL
) {
2173 DbgLog((LOG_ERROR
,1,TEXT("ACK! Can't get a DC for %hs"),
2174 szDeviceName
? szDeviceName
: "<NULL>"));
2177 DbgLog((LOG_TRACE
,3,TEXT("Created a DC for %s"),
2178 szDeviceName
? szDeviceName
: "<NULL>"));
2180 HBITMAP hbm
= CreateCompatibleBitmap(hdcDisplay
,1,1);
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
);
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
));
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
);
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
);
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
2250 // Until the input is exhausted, count the number of bits
2253 init
= init
& (init
- 1); // Turn off the bottommost bit
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
)
2275 ASSERT(Mask
!= 0x80000000);
2276 if (Mask
== 0x80000000) {
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");
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");
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");
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");
2330 // Are the number of planes equal to one
2332 if (pInput
->bmiHeader
.biPlanes
!= 1) {
2333 NOTE("Number of planes not one");
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");
2346 // Check the size of the structure
2348 if (pInput
->bmiHeader
.biSize
!= sizeof(BITMAPINFOHEADER
)) {
2349 NOTE("Size of BITMAPINFOHEADER wrong");
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");
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");
2380 // Check the number of palette colours is correct
2382 if (pInput
->bmiHeader
.biClrUsed
> PALETTE_ENTRIES(pInput
)) {
2383 NOTE("Too many colours in palette");
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");
2397 // Return the format of the video display
2399 const VIDEOINFO
*CImageDisplay::GetDisplayFormat()
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
)
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
);
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");
2489 // Is the display depth greater than the input format
2491 if (m_Display
.bmiHeader
.biBitCount
> pInput
->bmiHeader
.biBitCount
) {
2492 NOTE("(Video) Mismatch agreed");
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");
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");
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
,
2608 CAutoLock
cDisplayLock(this);
2613 // If this format is palettised then it doesn't have bit fields
2615 if (m_Display
.bmiHeader
.biBitCount
< 16) {
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
);
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
);
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
);