home *** CD-ROM | disk | FTP | other *** search
- //==========================================================================;
- //
- // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
- // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
- // PURPOSE.
- //
- // Copyright (c) 1992 - 1996 Microsoft Corporation. All Rights Reserved.
- //
- //--------------------------------------------------------------------------;
-
- // Generic window handler base class, December 1995
-
- #include <streams.h>
- #include <limits.h>
-
-
- // Initialise the under construction object pointer
- CBaseWindow *CBaseWindow::s_pInitBaseWindow = NULL;
-
-
- // Constructor
-
- CBaseWindow::CBaseWindow(void) :
- m_hInstance(g_hInst),
- m_hThread(INVALID_HANDLE_VALUE),
- m_ThreadID(0),
- m_hwnd(NULL),
- m_hdc(NULL),
- m_bActivated(FALSE),
- m_pClassName(NULL),
- m_ClassStyles(0),
- m_WindowStyles(0),
- m_WindowStylesEx(0),
- m_ThreadSignal(NOERROR),
- m_GoodByeMessage(0),
- m_ShowStageMessage(0),
- m_MemoryDC(NULL),
- m_hPalette(NULL),
- m_bBackground(FALSE)
- {
- }
-
-
- // Prepare a window by spinning off a worker thread to do the creation and
- // also poll the message input queue. We leave this to be called by derived
- // classes because they might want to override methods like MessageLoop and
- // InitialiseWindow, if we do this during construction they'll ALWAYS call
- // this base class methods. We make the worker thread create the window so
- // it owns it rather than the filter graph thread which is constructing us
-
- HRESULT CBaseWindow::PrepareWindow()
- {
- ASSERT(m_hwnd == NULL);
- ASSERT(m_hdc == NULL);
- m_ThreadSignal = NOERROR;
-
- // Get the derived object's window and class styles
-
- m_pClassName = GetClassWindowStyles(&m_ClassStyles,
- &m_WindowStyles,
- &m_WindowStylesEx);
- if (m_pClassName == NULL) {
- return E_FAIL;
- }
-
- // Register our special private messages
-
- m_GoodByeMessage = RegisterWindowMessage(m_pClassName);
- m_ShowStageMessage = RegisterWindowMessage(SHOWSTAGE);
-
- // Create a thread to look after the window
-
- m_hThread = CreateThread(NULL, // Security attributes
- (DWORD) 0, // Initial stack size
- WindowMessageLoop, // Thread start address
- (LPVOID) this, // Thread parameter
- (DWORD) 0, // Creation flags
- &m_ThreadID); // Thread identifier
-
- // If we couldn't create a thread the whole thing's off
-
- ASSERT(m_hThread);
- if (m_hThread == NULL) {
- return E_FAIL;
- }
-
- // Wait until the window has been initialised
- m_SyncWorker.Wait();
-
- // The thread needs to grab some resources when it is created, these may
- // or may not be available. If they are not then the thread fills in the
- // m_ThreadSignal and signals the constructor event to complete and then
- // exits. We test here to see if the construction completed ok and if not
- // we have ourselves destroyed which will close the open thread handle
-
- return m_ThreadSignal;
- }
-
-
- // Destructor just a placeholder so that we know it becomes virtual
- // Derived classes MUST call DoneWithWindow in their destructors so
- // that no messages arrive after the derived class constructor ends
-
- CBaseWindow::~CBaseWindow()
- {
- ASSERT(m_hwnd == NULL);
- ASSERT(m_hdc == NULL);
- }
-
-
- // Wait for the window thread to complete by sending a custom message - we
- // found that if a user has full drag on and is dragging the window at the
- // same time as the application destroys the renderer the m_GoodByeMessage
- // can be processed which means the worker thread calls UninitialiseWindow
- // and then PostQuitMessage, but the final WM_QUIT does not get send. The
- // solution is to save the window handle to keep sending it until it quits
-
- HRESULT CBaseWindow::DoneWithWindow()
- {
- if (m_hwnd == NULL) {
- return NOERROR;
- }
-
- HWND hwnd = m_hwnd;
- InactivateWindow();
- NOTE("Inactivated");
-
- // Reset the window styles before destruction
-
- SetWindowLong(m_hwnd,GWL_STYLE,m_WindowStyles);
- ASSERT(GetParent(m_hwnd) == NULL);
- NOTE1("Reset window styles %d",m_WindowStyles);
-
- // Tell the worker thread to destroy the window
-
- PostMessage(hwnd,m_GoodByeMessage,(WPARAM) 0,(LPARAM) 0);
- DWORD dwWait;
- for (;;) {
-
- dwWait = MsgWaitForMultipleObjects(1,&m_hThread,FALSE,500,QS_SENDMESSAGE);
- if (dwWait == WAIT_OBJECT_0) {
- break;
- } else if (dwWait == WAIT_TIMEOUT) {
- NOTE("Waiting for thread termination got WAIT_TIMEOUT");
- PostMessage(hwnd,m_GoodByeMessage,(WPARAM) 0,(LPARAM) 0);
- } else if (dwWait == WAIT_OBJECT_0 + 1) {
- // interthread sendmessage - to process it, we need to look at the
- // message queue
- MSG msg;
- PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
- }
- }
- EXECUTE_ASSERT(CloseHandle(m_hThread));
-
- // Reset our state so we can be prepared again
-
- m_hThread = INVALID_HANDLE_VALUE;
- m_ThreadID = 0;
- m_pClassName = NULL;
- m_ClassStyles = 0;
- m_WindowStyles = 0;
- m_WindowStylesEx = 0;
- m_GoodByeMessage = 0;
- m_ShowStageMessage = 0;
-
- return NOERROR;
- }
-
-
- // Called at the end to put the window in an inactive state. The pending list
- // will always have been cleared by this time so event if the worker thread
- // gets has been signaled and gets in to render something it will find both
- // the state has been changed and that there are no available sample images
- // Since we wait on the window thread to complete we don't lock the object
-
- HRESULT CBaseWindow::InactivateWindow()
- {
- // Has the window been activated
- if (m_bActivated == FALSE) {
- return S_FALSE;
- }
-
- m_bActivated = FALSE;
- ShowWindow(m_hwnd,SW_HIDE);
- return NOERROR;
- }
-
-
- // This displays a normal window. We ask the base window class for default
- // sizes which unless overriden will return DEFWIDTH and DEFHEIGHT. We go
- // through a couple of extra hoops to get the client area the right size
- // as the object specifies which accounts for the AdjustWindowRectEx calls
- // We also DWORD align the left and top coordinates of the window here to
- // maximise the chance of being able to use DCI/DirectDraw primary surface
-
- HRESULT CBaseWindow::ActivateWindow()
- {
- // Has the window been sized and positioned already
-
- if (m_bActivated == TRUE || GetParent(m_hwnd) != NULL) {
-
- SetWindowPos(m_hwnd, // Our window handle
- HWND_TOP, // Put it at the top
- 0, 0, 0, 0, // Leave in current position
- SWP_NOMOVE | // Don't change it's place
- SWP_NOSIZE); // Change Z-order only
-
- return S_FALSE;
- }
-
- // Calculate the desired client rectangle
-
- RECT WindowRect, ClientRect = GetDefaultRect();
- GetWindowRect(m_hwnd,&WindowRect);
- AdjustWindowRectEx(&ClientRect,GetWindowLong(m_hwnd,GWL_STYLE),
- FALSE,GetWindowLong(m_hwnd,GWL_EXSTYLE));
-
- // Align left and top edges on DWORD boundaries
-
- UINT WindowFlags = (SWP_NOACTIVATE | SWP_FRAMECHANGED);
- WindowRect.left -= (WindowRect.left & 3);
- WindowRect.top -= (WindowRect.top & 3);
-
- SetWindowPos(m_hwnd, // Window handle
- HWND_TOP, // Put it at the top
- WindowRect.left, // Align left edge
- WindowRect.top, // And also top place
- WIDTH(&ClientRect), // Horizontal size
- HEIGHT(&ClientRect), // Vertical size
- WindowFlags); // Don't show window
-
- m_bActivated = TRUE;
- return NOERROR;
- }
-
-
- // This can be used to DWORD align the window for maximum performance
-
- HRESULT CBaseWindow::PerformanceAlignWindow()
- {
- RECT ClientRect,WindowRect;
- GetWindowRect(m_hwnd,&WindowRect);
- ASSERT(m_bActivated == TRUE);
-
- // Don't do this if we're owned
-
- if (GetParent(m_hwnd)) {
- return NOERROR;
- }
-
- // Align left and top edges on DWORD boundaries
-
- GetClientRect(m_hwnd, &ClientRect);
- MapWindowPoints(m_hwnd, HWND_DESKTOP, (LPPOINT) &ClientRect, 2);
- WindowRect.left -= (ClientRect.left & 3);
- WindowRect.top -= (ClientRect.top & 3);
- UINT WindowFlags = (SWP_NOACTIVATE | SWP_NOSIZE);
-
- SetWindowPos(m_hwnd, // Window handle
- HWND_TOP, // Put it at the top
- WindowRect.left, // Align left edge
- WindowRect.top, // And also top place
- (int) 0,(int) 0, // Ignore these sizes
- WindowFlags); // Don't show window
-
- return NOERROR;
- }
-
-
- // Install a palette into the base window - we may be called by a different
- // thread to the one that owns the window. Therefore we cannot realise our
- // palettes in this function (or called functions) as it may cause an inter
- // thread send message to our window thread. Therefore rather than sending
- // a WM_QUERYNEWPALETTE to the window to realise the palette we'll post it
-
- HRESULT CBaseWindow::SetPalette(HPALETTE hPalette)
- {
- // We must own the window lock during the change
-
- CAutoLock cWindowLock(&m_WindowLock);
- ASSERT(hPalette);
- m_hPalette = hPalette;
-
- // Select the palette into the device contexts
-
- SelectPalette(m_hdc,m_hPalette,m_bBackground);
- SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);
- PostMessage(m_hwnd,WM_QUERYNEWPALETTE,0,0);
-
- return NOERROR;
- }
-
-
- // Realise our palettes in the window and device contexts
-
- HRESULT CBaseWindow::DoRealisePalette()
- {
- CAutoLock cWindowLock(&m_WindowLock);
- if (m_hPalette == NULL) {
- return NOERROR;
- }
-
- // Realize on the window thread
-
- SelectPalette(m_hdc,m_hPalette,m_bBackground);
- RealizePalette(m_hdc);
- SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);
- RealizePalette(m_MemoryDC);
- GdiFlush();
- return NOERROR;
- }
-
-
- // This is the global window procedure
-
- LRESULT CALLBACK WndProc(HWND hwnd, // Window handle
- UINT uMsg, // Message ID
- WPARAM wParam, // First parameter
- LPARAM lParam) // Other parameter
- {
- // Get the window long that holds our window object pointer
- // If it is NULL then we are initialising the window in which
- // case the object pointer is stored in static class variable
-
- CBaseWindow *pBaseWindow = (CBaseWindow *)GetWindowLong(hwnd,0);
- if (pBaseWindow == NULL) {
- ASSERT(CBaseWindow::s_pInitBaseWindow);
- pBaseWindow = CBaseWindow::s_pInitBaseWindow;
- }
- return pBaseWindow->OnReceiveMessage(hwnd,uMsg,wParam,lParam);
- }
-
-
- // When the window size changes we adjust our member variables that
- // contain the dimensions of the client rectangle for our window so
- // that we come to render an image we will know whether to stretch
-
- BOOL CBaseWindow::OnSize(LONG Width, LONG Height)
- {
- m_Width = Width;
- m_Height = Height;
- return TRUE;
- }
-
-
- // This function handles the WM_CLOSE message
-
- BOOL CBaseWindow::OnClose()
- {
- ShowWindow(m_hwnd,SW_HIDE);
- return TRUE;
- }
-
-
- // This is called by the worker window thread when it receives a terminate
- // message from the window object destructor to delete all the resources we
- // allocated during initialisation. By the time the worker thread exits all
- // processing will have been completed as the source filter disconnection
- // flushes the image pending sample, therefore the GdiFlush should succeed
-
- HRESULT CBaseWindow::UninitialiseWindow()
- {
- // Have we already cleaned up
-
- if (m_hwnd == NULL) {
- ASSERT(m_hdc == NULL);
- ASSERT(m_MemoryDC == NULL);
- return NOERROR;
- }
-
- // Release the window resources
-
- EXECUTE_ASSERT(GdiFlush());
- EXECUTE_ASSERT(ReleaseDC(m_hwnd,m_hdc));
- EXECUTE_ASSERT(DeleteDC(m_MemoryDC));
-
- // Reset the window variables
-
- m_hdc = NULL;
- m_hwnd = NULL;
- m_MemoryDC = NULL;
- return NOERROR;
- }
-
-
- // This is called by the worker window thread after it has created the main
- // window and it wants to initialise the rest of the owner objects window
- // variables such as the device contexts. We execute this function with the
- // critical section still locked. Nothing in this function must generate any
- // SendMessage calls to the window because this is executing on the window
- // thread so the message will never be processed and we will deadlock
-
- HRESULT CBaseWindow::InitialiseWindow(HWND hwnd)
- {
- // Initialise the window variables
-
- m_hwnd = hwnd;
- m_hdc = GetDC(hwnd);
- m_MemoryDC = CreateCompatibleDC(m_hdc);
-
- SetStretchBltMode(m_hdc,COLORONCOLOR);
- SetStretchBltMode(m_MemoryDC,COLORONCOLOR);
-
- // Quick sanity check
-
- ASSERT(m_hdc != NULL);
- return NOERROR;
- }
-
-
- // This is the windows message loop for our worker thread. It does a loop
- // processing and dispatching messages until it receives a WM_QUIT message
- // which will normally be generated through the owning object's destructor
-
- HRESULT CBaseWindow::MessageLoop()
- {
- MSG Message;
-
- while (GetMessage(&Message,NULL,0,0))
- {
- TranslateMessage(&Message);
- DispatchMessage(&Message);
- }
- return NOERROR;
- }
-
-
- // This creates a normal window and processes messages on a separate thread.
- // We use a mutex object that serialises window creation globally so we can
- // ensure that odd timing problems do not occur when registering the window.
- // We would still need to serialise access to the s_pInitVideoWindow class
- // field (albeit only in each process) because it is static. During window
- // construction we set the object pointer that owns the window in a global
- // variable so that all the window messages that arrive at the input queue
- // can be passed onto the correct object. Once the window has been created
- // we put the this pointer into a window LONG field where it is retrieved
- // in the message procedure. The constructor for the window class must be
- // signalled when we can let them continue, this is done through a member
- // variable (m_SyncWorker) which we can access as we are a friend method
-
- DWORD __stdcall CBaseWindow::WindowMessageLoop(LPVOID lpvThreadParm)
- {
- CBaseWindow *pBaseWindow; // The owner window object
- HANDLE WindowMutex; // Serialises object creation
- WNDCLASS wndclass; // Used to register classes
- BOOL bRegistered; // Is this class registered
- HWND hwnd; // Handle to our window
-
- // Cast the thread parameter to be our owner object
- pBaseWindow = (CBaseWindow *) lpvThreadParm;
-
- // Create/open the window mutex object
-
- WindowMutex = CreateMutex(NULL,FALSE,pBaseWindow->m_pClassName);
- if (WindowMutex == NULL) {
- DWORD Error = GetLastError();
- pBaseWindow->m_ThreadSignal = HRESULT_FROM_WIN32(Error);
- pBaseWindow->m_SyncWorker.Set();
- ExitThread(FALSE);
- }
-
- WaitForSingleObject(WindowMutex,INFINITE);
- CBaseWindow::s_pInitBaseWindow = pBaseWindow;
-
- bRegistered = GetClassInfo(pBaseWindow->m_hInstance, // Module instance
- pBaseWindow->m_pClassName, // Window class
- &wndclass); // Info structure
-
- if (bRegistered == FALSE) {
-
- // Register the renderer window class
-
- wndclass.lpszClassName = pBaseWindow->m_pClassName;
- wndclass.style = pBaseWindow->m_ClassStyles;
- wndclass.lpfnWndProc = (WNDPROC) WndProc;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = sizeof(CBaseWindow *);
- wndclass.hInstance = pBaseWindow->m_hInstance;
- wndclass.hIcon = NULL;
- wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
- wndclass.hbrBackground = (HBRUSH) NULL;
- wndclass.lpszMenuName = NULL;
-
- RegisterClass(&wndclass);
- }
-
- // Create the frame window
-
- hwnd = CreateWindowEx(pBaseWindow->m_WindowStylesEx, // Extended styles
- pBaseWindow->m_pClassName, // Registered name
- TEXT("ActiveMovie Window"), // Window title
- pBaseWindow->m_WindowStyles, // Window styles
- CW_USEDEFAULT, // Start x position
- CW_USEDEFAULT, // Start y position
- DEFWIDTH, // Window width
- DEFHEIGHT, // Window height
- NULL, // Parent handle
- NULL, // Menu handle
- pBaseWindow->m_hInstance, // Instance handle
- &pBaseWindow); // Creation data
-
- // If we failed signal an error to the object constructor (based on the
- // last Win32 error on this thread) then signal the constructor thread
- // to continue, release the mutex to let others have a go and exit
-
- if (hwnd == NULL) {
- DWORD Error = GetLastError();
- pBaseWindow->m_ThreadSignal = HRESULT_FROM_WIN32(Error);
- pBaseWindow->m_SyncWorker.Set();
- EXECUTE_ASSERT(ReleaseMutex(WindowMutex));
- EXECUTE_ASSERT(CloseHandle(WindowMutex));
- ExitThread(FALSE);
- }
-
- // Set the window LONG to be the object who created us
- SetWindowLong(hwnd, (DWORD) 0, (LONG) pBaseWindow);
- pBaseWindow->s_pInitBaseWindow = NULL;
-
- // Initialise the window and then signal the constructor so that it can
- // continue and then finally unlock the object's critical section. The
- // window class is left registered even after we terminate the thread
- // as we don't know when the last window has been closed. So we allow
- // the operating system to free the class resources as appropriate
-
- pBaseWindow->InitialiseWindow(hwnd);
- pBaseWindow->m_SyncWorker.Set();
- EXECUTE_ASSERT(ReleaseMutex(WindowMutex));
- EXECUTE_ASSERT(CloseHandle(WindowMutex));
-
- pBaseWindow->MessageLoop();
- ExitThread(TRUE);
- return TRUE;
- }
-
-
- // The base class provides some default handling and calls DefWindowProc
-
- LRESULT CBaseWindow::OnReceiveMessage(HWND hwnd, // Window handle
- UINT uMsg, // Message ID
- WPARAM wParam, // First parameter
- LPARAM lParam) // Other parameter
- {
- ASSERT(IsWindow(hwnd));
-
- // We receive a terminate window message (synchronously) from the
- // window object destructor in which case we do actually destroy
- // the window and complete the process in the WM_DESTROY message
-
- if (uMsg == m_GoodByeMessage) {
- DestroyWindow(m_hwnd);
- PostQuitMessage(FALSE);
- return (LRESULT) 0;
- }
-
- // This is sent by the IVideoWindow SetWindowForeground method. If the
- // window is invisible we will show it and make it topmost without the
- // foreground focus. If the window is visible it will also be made the
- // topmost window without the foreground focus. If wParam is TRUE then
- // for both cases the window will be forced into the foreground focus
-
- if (uMsg == m_ShowStageMessage) {
-
- BOOL bVisible = IsWindowVisible(hwnd);
- SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW |
- (bVisible ? SWP_NOACTIVATE : 0));
-
- // Should we bring the window to the foreground
- if (wParam == TRUE) {
- SetForegroundWindow(hwnd);
- }
- return (LRESULT) 1;
- }
-
- switch (uMsg) {
-
- // Repaint the window if the system colours change
-
- case WM_SYSCOLORCHANGE:
-
- InvalidateRect(hwnd,NULL,FALSE);
- return (LRESULT) 1;
-
- // Somebody is or has changed the palette
-
- case WM_PALETTECHANGED:
- case WM_PALETTEISCHANGING:
-
- OnPaletteChange((HWND)wParam,uMsg);
- return (LRESULT) 0;
-
- // We are about to receive the keyboard focus so we ask GDI to realise
- // our logical palette again and hopefully it will be fully installed
- // without any mapping having to be done during any picture rendering
-
- case WM_QUERYNEWPALETTE:
-
- return OnPaletteChange(m_hwnd,uMsg);
-
- // Store the width and height as useful base class members
-
- case WM_SIZE:
-
- OnSize(LOWORD(lParam), HIWORD(lParam));
- return (LRESULT) 0;
-
- // Intercept the WM_CLOSE messages to hide the window
-
- case WM_CLOSE:
-
- OnClose();
- return (LRESULT) 0;
-
- // Post the final WM_QUIT message to the window queue
-
- case WM_DESTROY:
-
- UninitialiseWindow();
- PostQuitMessage(FALSE);
- return (LRESULT) 0;
- }
- return DefWindowProc(hwnd,uMsg,wParam,lParam);
- }
-
-
- // This handles the palette change messages, if we do realise our palette then
- // we return TRUE otherwise we return FALSE. If we are the current foreground
- // window then we get first choice in the system palette entries although it
- // doesn't guarantee that our requirements will be met. Best performance is
- // obtained when our logical palette includes the standard VGA colours as it
- // virtually ensures any image will not have to be mapped during display. We
- // first check that the window handle is non NULL, if not then we install our
- // and realize our palettes again (by calling our DoRealisePalette method)
-
- LRESULT CBaseWindow::OnPaletteChange(HWND hwnd,UINT Message)
- {
- // First check we are not changing the palette during closedown
-
- if (m_hwnd == NULL || hwnd == NULL) {
- return (LRESULT) 0;
- }
-
- // Should we realise our palette again
-
- if (Message == WM_QUERYNEWPALETTE || hwnd != m_hwnd) {
- DoRealisePalette();
- }
-
- // Should we redraw the window with the new palette
-
- if (Message == WM_PALETTECHANGED) {
- InvalidateRect(m_hwnd,NULL,FALSE);
- }
- return (LRESULT) 1;
- }
-
-
- // Return the default window rectangle
-
- RECT CBaseWindow::GetDefaultRect()
- {
- RECT DefaultRect = {0,0,DEFWIDTH,DEFHEIGHT};
- ASSERT(m_hwnd);
- ASSERT(m_hdc);
- return DefaultRect;
- }
-
-
- // Return the current window width
-
- LONG CBaseWindow::GetWindowWidth()
- {
- ASSERT(m_hwnd);
- ASSERT(m_hdc);
- return m_Width;
- }
-
-
- // Return the current window height
-
- LONG CBaseWindow::GetWindowHeight()
- {
- ASSERT(m_hwnd);
- ASSERT(m_hdc);
- return m_Height;
- }
-
-
- // Return the window handle
-
- HWND CBaseWindow::GetWindowHWND()
- {
- ASSERT(m_hwnd);
- ASSERT(m_hdc);
- return m_hwnd;
- }
-
-
- // Return the window drawing device context
-
- HDC CBaseWindow::GetWindowHDC()
- {
- ASSERT(m_hwnd);
- ASSERT(m_hdc);
- return m_hdc;
- }
-
-
- // Return the offscreen window drawing device context
-
- HDC CBaseWindow::GetMemoryHDC()
- {
- ASSERT(m_hwnd);
- ASSERT(m_MemoryDC);
- return m_MemoryDC;
- }
-
-
- // This is available to clients who want to change the window visiblity. It's
- // little more than an indirection to the Win32 ShowWindow although these is
- // some benefit in going through here as this function may change sometime
-
- HRESULT CBaseWindow::DoShowWindow(LONG ShowCmd)
- {
- ShowWindow(m_hwnd,ShowCmd);
- return NOERROR;
- }
-
-
- // Generate a WM_PAINT message for the video window
-
- void CBaseWindow::PaintWindow(BOOL bErase)
- {
- InvalidateRect(m_hwnd,NULL,bErase);
- }
-
-
- // Allow an application to have us set the video window in the foreground. We
- // have this because it is difficult for one thread to do do this to a window
- // owned by another thread. Rather than expose the message we use to execute
- // the inter thread send message we provide the interface function. All we do
- // is to SendMessage to the video window renderer thread with a WM_SHOWSTAGE
-
- void CBaseWindow::DoSetWindowForeground(BOOL bFocus)
- {
- SendMessage(m_hwnd,m_ShowStageMessage,(WPARAM) bFocus,(LPARAM) 0);
- }
-
-
- // Constructor initialises the owning object pointer. Since we are a worker
- // class for the main window object we have relatively few state variables to
- // look after. We are given device context handles to use later on as well as
- // the source and destination rectangles (but reset them here just in case)
-
- CDrawImage::CDrawImage(CBaseWindow *pBaseWindow) :
- m_pBaseWindow(pBaseWindow),
- m_hdc(NULL),
- m_MemoryDC(NULL),
- m_bStretch(FALSE),
- m_pMediaType(NULL),
- m_bUsingImageAllocator(FALSE)
- {
- ASSERT(pBaseWindow);
- ResetPaletteVersion();
- SetRectEmpty(&m_TargetRect);
- SetRectEmpty(&m_SourceRect);
-
- m_perfidRenderTime = MSR_REGISTER("Single Blt time");
- }
-
-
- // Overlay the image time stamps on the picture. Access to this method is
- // serialised by the caller. We display the sample start and end times on
- // top of the video using TextOut on the device context we are handed. If
- // there isn't enough room in the window for the times we don't show them
-
- void CDrawImage::DisplaySampleTimes(IMediaSample *pSample)
- {
- TCHAR szTimes[TIMELENGTH]; // Time stamp strings
- ASSERT(pSample); // Quick sanity check
- RECT ClientRect; // Client window size
- SIZE Size; // Size of text output
-
- // Get the time stamps and window size
-
- pSample->GetTime((REFERENCE_TIME*)&m_StartSample, (REFERENCE_TIME*)&m_EndSample);
- HWND hwnd = m_pBaseWindow->GetWindowHWND();
- EXECUTE_ASSERT(GetClientRect(hwnd,&ClientRect));
-
- // Format the sample time stamps
-
- wsprintf(szTimes,TEXT("%08d : %08d"),
- m_StartSample.Millisecs(),
- m_EndSample.Millisecs());
-
- ASSERT(lstrlen(szTimes) < TIMELENGTH);
-
- // Put the times in the middle at the bottom of the window
-
- GetTextExtentPoint32(m_hdc,szTimes,lstrlen(szTimes),&Size);
- INT XPos = ((ClientRect.right - ClientRect.left) - Size.cx) / 2;
- INT YPos = ((ClientRect.bottom - ClientRect.top) - Size.cy) * 4 / 5;
-
- // Check the window is big enough to have sample times displayed
-
- if ((XPos > 0) && (YPos > 0)) {
- TextOut(m_hdc,XPos,YPos,szTimes,lstrlen(szTimes));
- }
- }
-
-
- // This is called when the drawing code sees that the image has a down level
- // palette cookie. We simply call the SetDIBColorTable Windows API with the
- // palette that is found after the BITMAPINFOHEADER - we return no errors
-
- void CDrawImage::UpdateColourTable(HDC hdc,BITMAPINFOHEADER *pbmi)
- {
- ASSERT(pbmi->biClrUsed);
- RGBQUAD *pColourTable = (RGBQUAD *)(pbmi+1);
-
- // Set the new palette in the device context
-
- UINT uiReturn = SetDIBColorTable(hdc,(UINT) 0,
- pbmi->biClrUsed,
- pColourTable);
-
- // Should always succeed but check in debug builds
- ASSERT(uiReturn == pbmi->biClrUsed);
- }
-
-
- // No source rectangle scaling is done by the base class
-
- RECT CDrawImage::ScaleSourceRect(const RECT *pSource)
- {
- ASSERT(pSource);
- return *pSource;
- }
-
-
- // This is called when the funky output pin uses our allocator. The samples we
- // allocate are special because the memory is shared between us and GDI thus
- // removing one copy when we ask for the image to be rendered. The source type
- // information is in the main renderer m_mtIn field which is initialised when
- // the media type is agreed in SetMediaType, the media type may be changed on
- // the fly if, for example, the source filter needs to change the palette
-
- void CDrawImage::FastRender(IMediaSample *pMediaSample)
- {
- BITMAPINFOHEADER *pbmi; // Image format data
- DIBDATA *pDibData; // Stores DIB information
- BYTE *pImage; // Pointer to image data
- HBITMAP hOldBitmap; // Store the old bitmap
- CImageSample *pSample; // Pointer to C++ object
-
- ASSERT(m_pMediaType);
-
- // From the untyped source format block get the VIDEOINFO and subsequently
- // the BITMAPINFOHEADER structure. We can cast the IMediaSample interface
- // to a CImageSample object so we can retrieve it's DIBSECTION details
-
- pbmi = HEADER(m_pMediaType->Format());
- pSample = (CImageSample *) pMediaSample;
- pDibData = pSample->GetDIBData();
- hOldBitmap = (HBITMAP) SelectObject(m_MemoryDC,pDibData->hBitmap);
-
- // Get a pointer to the real image data
-
- HRESULT hr = pMediaSample->GetPointer(&pImage);
- if (FAILED(hr)) {
- return;
- }
-
- // Do we need to update the colour table, we increment our palette cookie
- // each time we get a dynamic format change. The sample palette cookie is
- // stored in the DIBDATA structure so we try to keep the fields in sync
- // By the time we get to draw the images the format change will be done
- // so all we do is ask the renderer for what it's palette version is
-
- if (pDibData->PaletteVersion < GetPaletteVersion()) {
- ASSERT(pbmi->biBitCount <= iPALETTE);
- UpdateColourTable(m_MemoryDC,pbmi);
- pDibData->PaletteVersion = GetPaletteVersion();
- }
-
- // This allows derived classes to change the source rectangle that we do
- // the drawing with. For example a renderer may ask a codec to stretch
- // the video from 320x240 to 640x480, in which case the source we see in
- // here will still be 320x240, although the source we want to draw with
- // should be scaled up to 640x480. The base class implementation of this
- // method does nothing but return the same rectangle as we are passed in
-
- RECT SourceRect = ScaleSourceRect(&m_SourceRect);
-
- // Is the window the same size as the video
-
- if (m_bStretch == FALSE) {
-
- // Put the image straight into the window
-
- BitBlt(
- (HDC) m_hdc, // Target device HDC
- m_TargetRect.left, // X sink position
- m_TargetRect.top, // Y sink position
- m_TargetRect.right - m_TargetRect.left, // Destination width
- m_TargetRect.bottom - m_TargetRect.top, // Destination height
- m_MemoryDC, // Source device context
- SourceRect.left, // X source position
- SourceRect.top, // Y source position
- SRCCOPY); // Simple copy
-
- } else {
-
- // Stretch the image when copying to the window
-
- StretchBlt(
- (HDC) m_hdc, // Target device HDC
- m_TargetRect.left, // X sink position
- m_TargetRect.top, // Y sink position
- m_TargetRect.right - m_TargetRect.left, // Destination width
- m_TargetRect.bottom - m_TargetRect.top, // Destination height
- m_MemoryDC, // Source device HDC
- SourceRect.left, // X source position
- SourceRect.top, // Y source position
- SourceRect.right - SourceRect.left, // Source width
- SourceRect.bottom - SourceRect.top, // Source height
- SRCCOPY); // Simple copy
- }
-
- // This displays the sample times over the top of the image. This used to
- // draw the times into the offscreen device context however that actually
- // writes the text into the image data buffer which may not be writable
-
- #ifdef DEBUG
- DisplaySampleTimes(pMediaSample);
- #endif
-
- // Put the old bitmap back into the device context so we don't leak
- SelectObject(m_MemoryDC,hOldBitmap);
- }
-
-
- // This is called when there is a sample ready to be drawn, unfortunately the
- // output pin was being rotten and didn't choose our super excellent shared
- // memory DIB allocator so we have to do this slow render using boring old GDI
- // SetDIBitsToDevice and StretchDIBits. The down side of using these GDI
- // functions is that the image data has to be copied across from our address
- // space into theirs before going to the screen (although in reality the cost
- // is small because all they do is to map the buffer into their address space)
-
- void CDrawImage::SlowRender(IMediaSample *pMediaSample)
- {
- // Get the BITMAPINFOHEADER for the connection
-
- ASSERT(m_pMediaType);
- BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format());
- BYTE *pImage;
-
- // Get the image data buffer
-
- HRESULT hr = pMediaSample->GetPointer(&pImage);
- if (FAILED(hr)) {
- return;
- }
-
- // This allows derived classes to change the source rectangle that we do
- // the drawing with. For example a renderer may ask a codec to stretch
- // the video from 320x240 to 640x480, in which case the source we see in
- // here will still be 320x240, although the source we want to draw with
- // should be scaled up to 640x480. The base class implementation of this
- // method does nothing but return the same rectangle as we are passed in
-
- RECT SourceRect = ScaleSourceRect(&m_SourceRect);
-
- // Is the window the same size as the video
-
- if (m_bStretch == FALSE) {
-
- // Put the image straight into the window
-
- SetDIBitsToDevice(
- (HDC) m_hdc, // Target device HDC
- m_TargetRect.left, // X sink position
- m_TargetRect.top, // Y sink position
- m_TargetRect.right - m_TargetRect.left, // Destination width
- m_TargetRect.bottom - m_TargetRect.top, // Destination height
- SourceRect.left, // X source position
- SourceRect.top, // Y source position
- (UINT) 0, // Start scan line
- pbmi->biHeight, // Scan lines present
- pImage, // Image data
- (BITMAPINFO *) pbmi, // DIB header
- DIB_RGB_COLORS); // Type of palette
-
- } else {
-
- // Stretch the image when copying to the window
-
- StretchDIBits(
- (HDC) m_hdc, // Target device HDC
- m_TargetRect.left, // X sink position
- m_TargetRect.top, // Y sink position
- m_TargetRect.right - m_TargetRect.left, // Destination width
- m_TargetRect.bottom - m_TargetRect.top, // Destination height
- SourceRect.left, // X source position
- SourceRect.top, // Y source position
- SourceRect.right - SourceRect.left, // Source width
- SourceRect.bottom - SourceRect.top, // Source height
- pImage, // Image data
- (BITMAPINFO *) pbmi, // DIB header
- DIB_RGB_COLORS, // Type of palette
- SRCCOPY); // Simple image copy
- }
-
- // This shows the sample reference times over the top of the image which
- // looks a little flickery. I tried using GdiSetBatchLimit and GdiFlush to
- // control the screen updates but it doesn't quite work as expected and
- // only partially reduces the flicker. I also tried using a memory context
- // and combining the two in that before doing a final BitBlt operation to
- // the screen, unfortunately this has considerable performance penalties
- // and also means that this code is not executed when compiled retail
-
- #ifdef DEBUG
- DisplaySampleTimes(pMediaSample);
- #endif
- }
-
-
- // This is called with an IMediaSample interface on the image to be drawn. We
- // decide on the drawing mechanism based on who's allocator we are using. We
- // may be called when the window wants an image painted by WM_PAINT messages
- // We can't realise the palette here because we have the renderer lock, any
- // call to realise may cause an interthread send message to the window thread
- // which may in turn be waiting to get the renderer lock before servicing it
-
- BOOL CDrawImage::DrawImage(IMediaSample *pMediaSample)
- {
- ASSERT(m_hdc);
- ASSERT(m_MemoryDC);
- NotifyStartDraw();
-
- // If the output pin used our allocator then the samples passed are in
- // fact CVideoSample objects that contain CreateDIBSection data that we
- // use to do faster image rendering, they may optionally also contain a
- // DirectDraw surface pointer in which case we do not do the drawing
-
- if (m_bUsingImageAllocator == FALSE) {
- SlowRender(pMediaSample);
- EXECUTE_ASSERT(GdiFlush());
- NotifyEndDraw();
- return TRUE;
- }
-
- // This is a DIBSECTION buffer
-
- FastRender(pMediaSample);
- EXECUTE_ASSERT(GdiFlush());
- NotifyEndDraw();
- return TRUE;
- }
-
-
- // This is called by the owning window object after it has created the window
- // and it's drawing contexts. We are constructed with the base window we'll
- // be drawing into so when given the notification we retrive the device HDCs
- // to draw with. We cannot call these in our constructor as they are virtual
-
- void CDrawImage::SetDrawContext()
- {
- m_MemoryDC = m_pBaseWindow->GetMemoryHDC();
- m_hdc = m_pBaseWindow->GetWindowHDC();
- }
-
-
- // This is called to set the target rectangle in the video window, it will be
- // called whenever a WM_SIZE message is retrieved from the message queue. We
- // simply store the rectangle and use it later when we do the drawing calls
-
- void CDrawImage::SetTargetRect(RECT *pTargetRect)
- {
- ASSERT(pTargetRect);
- m_TargetRect = *pTargetRect;
- SetStretchMode();
- }
-
-
- // Return the current target rectangle
-
- void CDrawImage::GetTargetRect(RECT *pTargetRect)
- {
- ASSERT(pTargetRect);
- *pTargetRect = m_TargetRect;
- }
-
-
- // This is called when we want to change the section of the image to draw. We
- // use this information in the drawing operation calls later on. We must also
- // see if the source and destination rectangles have the same dimensions. If
- // not we must stretch during the drawing rather than a direct pixel copy
-
- void CDrawImage::SetSourceRect(RECT *pSourceRect)
- {
- ASSERT(pSourceRect);
- m_SourceRect = *pSourceRect;
- SetStretchMode();
- }
-
-
- // Return the current source rectangle
-
- void CDrawImage::GetSourceRect(RECT *pSourceRect)
- {
- ASSERT(pSourceRect);
- *pSourceRect = m_SourceRect;
- }
-
-
- // This is called when either the source or destination rectanges change so we
- // can update the stretch flag. If the rectangles don't match we stretch the
- // video during the drawing otherwise we call the fast pixel copy functions
- // NOTE the source and/or the destination rectangle may be completely empty
-
- void CDrawImage::SetStretchMode()
- {
- // Calculate the overall rectangle dimensions
-
- LONG SourceWidth = m_SourceRect.right - m_SourceRect.left;
- LONG SinkWidth = m_TargetRect.right - m_TargetRect.left;
- LONG SourceHeight = m_SourceRect.bottom - m_SourceRect.top;
- LONG SinkHeight = m_TargetRect.bottom - m_TargetRect.top;
-
- m_bStretch = TRUE;
- if (SourceWidth == SinkWidth) {
- if (SourceHeight == SinkHeight) {
- m_bStretch = FALSE;
- }
- }
- }
-
-
- // Tell us whose allocator we are using. This should be called with TRUE if
- // the filter agrees to use an allocator based around the CImageAllocator
- // SDK base class - whose image buffers are made through CreateDIBSection.
- // Otherwise this should be called with FALSE and we will draw the images
- // using SetDIBitsToDevice and StretchDIBitsToDevice. None of these calls
- // can handle buffers which have non zero strides (like DirectDraw uses)
-
- void CDrawImage::NotifyAllocator(BOOL bUsingImageAllocator)
- {
- m_bUsingImageAllocator = bUsingImageAllocator;
- }
-
-
- // Are we using the image DIBSECTION allocator
-
- BOOL CDrawImage::UsingImageAllocator()
- {
- return m_bUsingImageAllocator;
- }
-
-
- // We need the media type of the connection so that we can get the BITMAPINFO
- // from it. We use that in the calls to draw the image such as StretchDIBits
- // and also when updating the colour table held in shared memory DIBSECTIONs
-
- void CDrawImage::NotifyMediaType(CMediaType *pMediaType)
- {
- m_pMediaType = pMediaType;
- }
-
-
- // We store in this object a cookie maintaining the current palette version.
- // Each time a palettised format is changed we increment this value so that
- // when we come to draw the images we look at the colour table value they
- // have and if less than the current we know to update it. This version is
- // only needed and indeed used when working with shared memory DIBSECTIONs
-
- LONG CDrawImage::GetPaletteVersion()
- {
- return m_PaletteVersion;
- }
-
-
- // Resets the current palette version number
-
- void CDrawImage::ResetPaletteVersion()
- {
- m_PaletteVersion = PALETTE_VERSION;
- }
-
-
- // Increment the current palette version
-
- void CDrawImage::IncrementPaletteVersion()
- {
- m_PaletteVersion++;
- }
-
-
- // Constructor must initialise the base allocator. Each sample we create has a
- // palette version cookie on board. When the source filter changes the palette
- // during streaming the window object increments an internal cookie counter it
- // keeps as well. When it comes to render the samples it looks at the cookie
- // values and if they don't match then it knows to update the sample's colour
- // table. However we always create samples with a cookie of PALETTE_VERSION
- // If there have been multiple format changes and we disconnect and reconnect
- // thereby causing the samples to be reallocated we will create them with a
- // cookie much lower than the current version, this isn't a problem since it
- // will be seen by the window object and the versions will then be updated
-
- CImageAllocator::CImageAllocator(CBaseFilter *pFilter,
- TCHAR *pName,
- HRESULT *phr) :
- CBaseAllocator(pName,NULL,phr),
- m_pFilter(pFilter)
- {
- ASSERT(phr);
- ASSERT(pFilter);
- }
-
-
- // Check our DIB buffers have been released
-
- CImageAllocator::~CImageAllocator()
- {
- ASSERT(m_bCommitted == FALSE);
- }
-
-
- // Called from destructor and also from base class to free resources. We work
- // our way through the list of media samples deleting the DIBSECTION created
- // for each. All samples should be back in our list so there is no chance a
- // filter is still using one to write on the display or hold on a pending list
-
- void CImageAllocator::Free()
- {
- ASSERT(m_lAllocated == m_lFree.GetCount());
- EXECUTE_ASSERT(GdiFlush());
- CImageSample *pSample;
- DIBDATA *pDibData;
-
- while (m_lFree.GetCount() != 0) {
- pSample = (CImageSample *) m_lFree.RemoveHead();
- pDibData = pSample->GetDIBData();
- EXECUTE_ASSERT(DeleteObject(pDibData->hBitmap));
- EXECUTE_ASSERT(CloseHandle(pDibData->hMapping));
- delete pSample;
- }
-
- m_lAllocated = 0;
- }
-
-
- // Prepare the allocator by checking all the input parameters
-
- STDMETHODIMP CImageAllocator::CheckSizes(ALLOCATOR_PROPERTIES *pRequest)
- {
- // Check we have a valid connection
-
- if (m_pMediaType == NULL) {
- return VFW_E_NOT_CONNECTED;
- }
-
- // NOTE We always create a DIB section with the source format type which
- // may contain a source palette. When we do the BitBlt drawing operation
- // the target display device may contain a different palette (we may not
- // have the focus) in which case GDI will do after the palette mapping
-
- VIDEOINFO *pVideoInfo = (VIDEOINFO *) m_pMediaType->Format();
-
- // When we call CreateDIBSection it implicitly maps only enough memory
- // for the image as defined by thee BITMAPINFOHEADER. If the user asks
- // for an image smaller than this then we reject the call, if they ask
- // for an image larger than this then we return what they can have
-
- if ((DWORD) pRequest->cbBuffer < pVideoInfo->bmiHeader.biSizeImage) {
- return E_INVALIDARG;
- }
-
- // Reject buffer prefixes
-
- if (pRequest->cbPrefix > 0) {
- return E_INVALIDARG;
- }
-
- pRequest->cbBuffer = pVideoInfo->bmiHeader.biSizeImage;
- return NOERROR;
- }
-
-
- // Agree the number of media sample buffers and their sizes. The base class
- // this allocator is derived from allows samples to be aligned only on byte
- // boundaries NOTE the buffers are not allocated until the Commit call
-
- STDMETHODIMP CImageAllocator::SetProperties(
- ALLOCATOR_PROPERTIES * pRequest,
- ALLOCATOR_PROPERTIES * pActual)
- {
- ALLOCATOR_PROPERTIES Adjusted = *pRequest;
-
- // Check the parameters fit with the current connection
-
- HRESULT hr = CheckSizes(&Adjusted);
- if (FAILED(hr)) {
- return hr;
- }
- return CBaseAllocator::SetProperties(&Adjusted, pActual);
- }
-
-
- // Commit the memory by allocating the agreed number of media samples. For
- // each sample we are committed to creating we have a CImageSample object
- // that we use to manage it's resources. This is initialised with a DIBDATA
- // structure that contains amongst other things the GDI DIBSECTION handle
- // We will access the renderer media type during this so we must have locked
- // (to prevent the format changing for example). The class overrides Commit
- // and Decommit to do this locking (base class Commit in turn calls Alloc)
-
- HRESULT CImageAllocator::Alloc(void)
- {
- ASSERT(m_pMediaType);
- CImageSample *pSample;
- DIBDATA DibData;
-
- // Check the base allocator says it's ok to continue
-
- HRESULT hr = CBaseAllocator::Alloc();
- if (FAILED(hr)) {
- return hr;
- }
-
- // We create a new memory mapped object although we don't map it into our
- // address space because GDI does that in CreateDIBSection. It is possible
- // that we run out of resources before creating all the samples in which
- // case the available sample list is left with those already created
-
- ASSERT(m_lAllocated == 0);
- while (m_lAllocated < m_lCount) {
-
- // Create and initialise a shared memory GDI buffer
-
- HRESULT hr = CreateDIB(m_lSize,DibData);
- if (FAILED(hr)) {
- return hr;
- }
-
- // Create the sample object and pass it the DIBDATA
-
- pSample = CreateImageSample(DibData.pBase,m_lSize);
- if (pSample == NULL) {
- EXECUTE_ASSERT(DeleteObject(DibData.hBitmap));
- EXECUTE_ASSERT(CloseHandle(DibData.hMapping));
- return E_OUTOFMEMORY;
- }
-
- // Add the completed sample to the available list
-
- pSample->SetDIBData(&DibData);
- m_lFree.Add(pSample);
- m_lAllocated++;
- }
- return NOERROR;
- }
-
-
- // We have a virtual method that allocates the samples so that a derived class
- // may override it and allocate more specialised sample objects. So long as it
- // derives its samples from CImageSample then all this code will still work ok
-
- CImageSample *CImageAllocator::CreateImageSample(LPBYTE pData,LONG Length)
- {
- HRESULT hr = NOERROR;
- CImageSample *pSample;
-
- // Allocate the new sample and check the return codes
-
- pSample = new CImageSample((CBaseAllocator *) this, // Base class
- NAME("Video sample"), // DEBUG name
- (HRESULT *) &hr, // Return code
- (LPBYTE) pData, // DIB address
- (LONG) Length); // Size of DIB
-
- if (pSample == NULL || FAILED(hr)) {
- delete pSample;
- return NULL;
- }
- return pSample;
- }
-
-
- // This function allocates a shared memory block for use by the source filter
- // generating DIBs for us to render. The memory block is created in shared
- // memory so that GDI doesn't have to copy the memory when we do a BitBlt
-
- HRESULT CImageAllocator::CreateDIB(LONG InSize,DIBDATA &DibData)
- {
- BITMAPINFO *pbmi; // Format information for pin
- BYTE *pBase; // Pointer to the actual image
- HANDLE hMapping; // Handle to mapped object
- HBITMAP hBitmap; // DIB section bitmap handle
-
- // Create a file mapping object and map into our address space
-
- hMapping = CreateFileMapping(hMEMORY, // Use system page file
- NULL, // No security attributes
- PAGE_READWRITE, // Full access to memory
- (DWORD) 0, // Less than 4Gb in size
- InSize, // Size of buffer
- NULL); // No name to section
- if (hMapping == NULL) {
- DWORD Error = GetLastError();
- return HRESULT_FROM_WIN32(Error);
- }
-
- // NOTE We always create a DIB section with the source format type which
- // may contain a source palette. When we do the BitBlt drawing operation
- // the target display device may contain a different palette (we may not
- // have the focus) in which case GDI will do after the palette mapping
-
- pbmi = (BITMAPINFO *) HEADER(m_pMediaType->Format());
- if (m_pMediaType == NULL) {
- DbgBreak("Invalid media type");
- }
-
- hBitmap = CreateDIBSection((HDC) NULL, // NO device context
- pbmi, // Format information
- DIB_RGB_COLORS, // Use the palette
- (VOID **) &pBase, // Pointer to image data
- hMapping, // Mapped memory handle
- (DWORD) 0); // Offset into memory
-
- if (hBitmap == NULL || pBase == NULL) {
- EXECUTE_ASSERT(CloseHandle(hMapping));
- DWORD Error = GetLastError();
- return HRESULT_FROM_WIN32(Error);
- }
-
- // Initialise the DIB information structure
-
- DibData.hBitmap = hBitmap;
- DibData.hMapping = hMapping;
- DibData.pBase = pBase;
- DibData.PaletteVersion = PALETTE_VERSION;
- GetObject(hBitmap,sizeof(DIBSECTION),(VOID *)&DibData.DibSection);
-
- return NOERROR;
- }
-
-
- // We use the media type during the DIBSECTION creation
-
- void CImageAllocator::NotifyMediaType(CMediaType *pMediaType)
- {
- m_pMediaType = pMediaType;
- }
-
-
- // Overriden to increment the owning object's reference count
-
- STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingAddRef()
- {
- return m_pFilter->AddRef();
- }
-
-
- // Overriden to decrement the owning object's reference count
-
- STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingRelease()
- {
- return m_pFilter->Release();
- }
-
-
- // If you derive a class from CMediaSample that has to transport specialised
- // member variables and entry points then there are three alternate solutions
- // The first is to create a memory buffer larger than actually required by the
- // sample and store your information either at the beginning of it or at the
- // end, the former being moderately safer allowing for misbehaving transform
- // filters. You then adjust the buffer address when you create the base media
- // sample. This has the disadvantage of breaking up the memory allocated to
- // the samples into separate blocks. The second solution is to implement a
- // class derived from CMediaSample and support additional interface(s) that
- // convey your private data. This means defining a custom interface. The final
- // alternative is to create a class that inherits from CMediaSample and adds
- // the private data structures, when you get an IMediaSample in your Receive()
- // call check to see if your allocator is being used, and if it is then cast
- // the IMediaSample into one of your objects. Additional checks can be made
- // to ensure the sample's this pointer is known to be one of your own objects
-
- CImageSample::CImageSample(CBaseAllocator *pAllocator,
- TCHAR *pName,
- HRESULT *phr,
- LPBYTE pBuffer,
- LONG length) :
- CMediaSample(pName,pAllocator,phr,pBuffer,length),
- m_bInit(FALSE)
- {
- ASSERT(pAllocator);
- ASSERT(pBuffer);
- }
-
-
- // Set the shared memory DIB information
-
- void CImageSample::SetDIBData(DIBDATA *pDibData)
- {
- ASSERT(pDibData);
- m_DibData = *pDibData;
- m_bInit = TRUE;
- }
-
-
- // Retrieve the shared memory DIB data
-
- DIBDATA *CImageSample::GetDIBData()
- {
- ASSERT(m_bInit == TRUE);
- return &m_DibData;
- }
-
-
- // This class handles the creation of a palette. It is fairly specialist and
- // is intended to simplify palette management for video renderer filters. It
- // is for this reason that the constructor requires three other objects with
- // which it interacts, namely a base media filter, a base window and a base
- // drawing object although the base window or the draw object may be NULL to
- // ignore that part of us. We try not to create and install palettes unless
- // absolutely necessary as they typically require WM_PALETTECHANGED messages
- // to be sent to every window thread in the system which is very expensive
-
- CImagePalette::CImagePalette(CBaseFilter *pBaseFilter,
- CBaseWindow *pBaseWindow,
- CDrawImage *pDrawImage) :
- m_pBaseWindow(pBaseWindow),
- m_pFilter(pBaseFilter),
- m_pDrawImage(pDrawImage),
- m_hPalette(NULL)
- {
- ASSERT(m_pFilter);
- }
-
-
- // Destructor
-
- CImagePalette::~CImagePalette()
- {
- ASSERT(m_hPalette == NULL);
- }
-
-
- // We allow dynamic format changes of the palette but rather than change the
- // palette every time we call this to work out whether an update is required.
- // If the original type didn't use a palette and the new one does (or vica
- // versa) then we return TRUE. If neither formats use a palette we'll return
- // FALSE. If both formats use a palette we compare their colours and return
- // FALSE if they match. This therefore short circuits palette creation unless
- // absolutely necessary since installing palettes is an expensive operation
-
- BOOL CImagePalette::ShouldUpdate(const VIDEOINFO *pNewInfo,
- const VIDEOINFO *pOldInfo)
- {
- // We may not have a current format yet
-
- if (pOldInfo == NULL) {
- return TRUE;
- }
-
- // Do both formats not require a palette
-
- if (ContainsPalette(pNewInfo) == FALSE) {
- if (ContainsPalette(pOldInfo) == FALSE) {
- return FALSE;
- }
- }
-
- // Compare the colours to see if they match
-
- DWORD VideoEntries = pNewInfo->bmiHeader.biClrUsed;
- if (ContainsPalette(pNewInfo) == TRUE)
- if (ContainsPalette(pOldInfo) == TRUE)
- if (pOldInfo->bmiHeader.biClrUsed == VideoEntries)
- if (pOldInfo->bmiHeader.biClrUsed > 0)
- if (memcmp((PVOID) GetBitmapPalette(pNewInfo),
- (PVOID) GetBitmapPalette(pOldInfo),
- VideoEntries * sizeof(RGBQUAD)) == 0) {
-
- return FALSE;
- }
- return TRUE;
- }
-
-
- // This is normally called when the input pin type is set to install a palette
- // We will typically be called from two different places. The first is when we
- // have negotiated a palettised media type after connection, the other is when
- // we receive a new type during processing with an updated palette in which
- // case we must remove and release the resources held by the current palette
-
- HRESULT CImagePalette::PreparePalette(const CMediaType *pmtNew,
- const CMediaType *pmtOld)
- {
- const VIDEOINFO *pNewInfo = (VIDEOINFO *) pmtNew->Format();
- const VIDEOINFO *pOldInfo = (VIDEOINFO *) pmtOld->Format();
- ASSERT(pNewInfo);
-
- // This is an performance optimisation, when we get a media type we check
- // to see if the format requires a palette change. If either we need one
- // when previously we didn't or vica versa then this returns TRUE, if we
- // previously needed a palette and we do now it compares their colours
-
- if (ShouldUpdate(pNewInfo,pOldInfo) == FALSE) {
- NOTE("No update needed");
- return S_FALSE;
- }
-
- // We must notify the filter graph that the application may have changed
- // the palette although in practice we don't bother checking to see if it
- // is really different. If it tries to get the palette either the window
- // or renderer lock will ensure it doesn't get in until we are finished
-
- RemovePalette();
- m_pFilter->NotifyEvent(EC_PALETTE_CHANGED,0,0);
-
- // Do we need a palette for the new format
-
- if (ContainsPalette(pNewInfo) == FALSE) {
- NOTE("New has no palette");
- return S_FALSE;
- }
-
- // If we're changing the palette on the fly then we increment our palette
- // cookie which is compared against the cookie also stored in all of our
- // DIBSECTION media samples. If they don't match when we come to draw it
- // then we know the sample is out of date and we'll update it's palette
-
- NOTE("Making new colour palette");
- m_hPalette = MakePalette(pNewInfo);
- ASSERT(m_hPalette != NULL);
-
- // The window in which the new palette is to be realised may be a NULL
- // pointer to signal that no window is in use, if so we don't call it
- // Some filters just want to use this object to create/manage palettes
-
- if (m_pBaseWindow) m_pBaseWindow->SetPalette(m_hPalette);
-
- // This is the only time where we need access to the draw object to say
- // to it that a new palette will be arriving on a sample real soon. The
- // constructor may take a NULL pointer in which case we don't call this
-
- if (m_pDrawImage) m_pDrawImage->IncrementPaletteVersion();
- return NOERROR;
- }
-
-
- // Helper function to copy a palette out of any kind of VIDEOINFO (ie it may
- // be YUV or true colour) into a palettised VIDEOINFO. We use this changing
- // palettes on DirectDraw samples as a source filter can attach a palette to
- // any buffer (eg YUV) and hand it back. We make a new palette out of that
- // format and then copy the palette colours into the current connection type
-
- HRESULT CImagePalette::CopyPalette(const CMediaType *pSrc,CMediaType *pDest)
- {
- // Reset the destination palette before starting
-
- VIDEOINFO *pDestInfo = (VIDEOINFO *) pDest->Format();
- pDestInfo->bmiHeader.biClrUsed = 0;
- pDestInfo->bmiHeader.biClrImportant = 0;
-
- // Does the destination have a palette
-
- if (PALETTISED(pDestInfo) == FALSE) {
- NOTE("No destination palette");
- return S_FALSE;
- }
-
- // Does the source contain a palette
-
- const VIDEOINFO *pSrcInfo = (VIDEOINFO *) pSrc->Format();
- if (ContainsPalette(pSrcInfo) == FALSE) {
- NOTE("No source palette");
- return S_FALSE;
- }
-
- // The number of colours may be zero filled
-
- DWORD PaletteEntries = pSrcInfo->bmiHeader.biClrUsed;
- if (PaletteEntries == 0) {
- DWORD Maximum = (1 << pSrcInfo->bmiHeader.biBitCount);
- NOTE1("Setting maximum colours (%d)",Maximum);
- PaletteEntries = Maximum;
- }
-
- // Make sure the destination has enough room for the palette
-
- ASSERT(pSrcInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);
- ASSERT(pSrcInfo->bmiHeader.biClrImportant <= PaletteEntries);
- ASSERT(pDestInfo->bmiColors == GetBitmapPalette(pDestInfo));
- pDestInfo->bmiHeader.biClrUsed = PaletteEntries;
- pDestInfo->bmiHeader.biClrImportant = pSrcInfo->bmiHeader.biClrImportant;
- ULONG BitmapSize = GetBitmapFormatSize(HEADER(pSrcInfo));
-
- if (pDest->FormatLength() < BitmapSize) {
- NOTE("Reallocating destination");
- pDest->ReallocFormatBuffer(BitmapSize);
- }
-
- // Now copy the palette colours across
-
- CopyMemory((PVOID) pDestInfo->bmiColors,
- (PVOID) GetBitmapPalette(pSrcInfo),
- PaletteEntries * sizeof(RGBQUAD));
-
- return NOERROR;
- }
-
-
- // This is normally called when the palette is changed (typically during a
- // dynamic format change) to remove any palette we previously installed. We
- // replace it (if necessary) in the video window with a standard VGA palette
- // that should always be available even if this is a true colour display
-
- HRESULT CImagePalette::RemovePalette()
- {
- // Do we have a palette to remove
-
- if (m_hPalette == NULL) {
- return NOERROR;
- }
-
- // Get a standard VGA colour palette
-
- HPALETTE hPalette = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
- ASSERT(hPalette);
-
- // Install the standard palette and delete ours. As in the previous method
- // we may not have been given a window in the constructor to use and if we
- // didn't then don't try to install the stock palette in it. This is used
- // by filters that have to create palettes but who do not draw using GDI
-
- if (m_pBaseWindow) {
- m_pBaseWindow->SetPalette(hPalette);
- }
-
- EXECUTE_ASSERT(DeleteObject(m_hPalette));
- m_hPalette = NULL;
- return NOERROR;
- }
-
-
- // Called to create a palette for the object, the data structure used by GDI
- // to describe a palette is a LOGPALETTE, this includes a variable number of
- // PALETTEENTRY fields which are the colours, we have to convert the RGBQUAD
- // colour fields we are handed in a BITMAPINFO from the media type into these
- // This handles extraction of palettes from true colour and YUV media formats
-
- HPALETTE CImagePalette::MakePalette(const VIDEOINFO *pVideoInfo)
- {
- ASSERT(ContainsPalette(pVideoInfo) == TRUE);
- ASSERT(pVideoInfo->bmiHeader.biClrUsed >= 0);
- ASSERT(pVideoInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);
- BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo);
-
- const RGBQUAD *pColours; // Pointer to the palette
- LOGPALETTE *lp; // Used to create a palette
- HPALETTE hPalette; // Logical palette object
-
- lp = (LOGPALETTE *) new BYTE[sizeof(LOGPALETTE) + SIZE_PALETTE];
- if (lp == NULL) {
- return NULL;
- }
-
- // Unfortunately for some hare brained reason a GDI palette entry (a
- // PALETTEENTRY structure) is different to a palette entry from a DIB
- // format (a RGBQUAD structure) so we have to do the field conversion
- // The VIDEOINFO containing the palette may be a true colour type so
- // we use GetBitmapPalette to skip over any bit fields if they exist
-
- lp->palVersion = PALVERSION;
- lp->palNumEntries = (USHORT) pHeader->biClrUsed;
- if (lp->palNumEntries == 0) lp->palNumEntries = (1 << pHeader->biBitCount);
- pColours = GetBitmapPalette(pVideoInfo);
-
- for (DWORD dwCount = 0;dwCount < lp->palNumEntries;dwCount++) {
- lp->palPalEntry[dwCount].peRed = pColours[dwCount].rgbRed;
- lp->palPalEntry[dwCount].peGreen = pColours[dwCount].rgbGreen;
- lp->palPalEntry[dwCount].peBlue = pColours[dwCount].rgbBlue;
- lp->palPalEntry[dwCount].peFlags = 0;
- }
-
- MakeIdentityPalette(lp->palPalEntry,lp->palNumEntries);
-
- // Create a logical palette
-
- hPalette = CreatePalette(lp);
- ASSERT(hPalette != NULL);
- delete[] lp;
- return hPalette;
- }
-
-
- // GDI does a fair job of compressing the palette entries you give it, so for
- // example if you have five entries with an RGB colour (0,0,0) it will remove
- // all but one of them. When you subsequently draw an image it will map from
- // your logical palette to the compressed device palette. This function looks
- // to see if it is trying to be an identity palette and if so sets the flags
- // field in the PALETTEENTRYs so they remain expanded to boost performance
-
- HRESULT CImagePalette::MakeIdentityPalette(PALETTEENTRY *pEntry,INT iColours)
- {
- PALETTEENTRY SystemEntries[10]; // System palette entries
- BOOL bIdentityPalette = TRUE; // Is an identity palette
- ASSERT(iColours <= iPALETTE_COLORS); // Should have a palette
-
- // Does this have the full colour range
-
- if (iColours < iPALETTE_COLORS) {
- return S_FALSE;
- }
-
- // Apparently some displays have odd numbers of system colours
-
- HDC hdc = GetDC(NULL);
- INT Reserved = GetDeviceCaps(hdc,NUMRESERVED);
- if (Reserved != 20) {
- ReleaseDC(NULL,hdc);
- return S_FALSE;
- }
-
- // Compare our palette against the first ten system entries. The reason I
- // don't do a memory compare between our two arrays of colours is because
- // I am not sure what will be in the flags fields for the system entries
-
- UINT Result = GetSystemPaletteEntries(hdc,0,10,SystemEntries);
- for (UINT Count = 0;Count < Result;Count++) {
- if (SystemEntries[Count].peRed != pEntry[Count].peRed ||
- SystemEntries[Count].peGreen != pEntry[Count].peGreen ||
- SystemEntries[Count].peBlue != pEntry[Count].peBlue) {
-
- bIdentityPalette = FALSE;
- }
- }
-
- // And likewise compare against the last ten entries
-
- Result = GetSystemPaletteEntries(hdc,246,10,SystemEntries);
- for (Count = 0;Count < Result;Count++) {
- if (SystemEntries[Count].peRed != pEntry[246 + Count].peRed ||
- SystemEntries[Count].peGreen != pEntry[246 + Count].peGreen ||
- SystemEntries[Count].peBlue != pEntry[246 + Count].peBlue) {
-
- bIdentityPalette = FALSE;
- }
- }
-
- // If not an identity palette then return S_FALSE
-
- ReleaseDC(NULL,hdc);
- if (bIdentityPalette == FALSE) {
- return S_FALSE;
- }
-
- // Set the non VGA entries so that GDI doesn't map them
-
- for (Count = 10;Count < 246;Count++) {
- pEntry[Count].peFlags = PC_NOCOLLAPSE;
- }
- return NOERROR;
- }
-
-
- // Constructor initialises the VIDEOINFO we keep storing the current display
- // format. The format can be changed at any time, to reset the format held
- // by us call the RefreshDisplayType directly (it's a public method). Since
- // more than one thread will typically call us (ie window threads resetting
- // the type and source threads in the type checking methods) we have a lock
-
- CImageDisplay::CImageDisplay()
- {
- RefreshDisplayType();
- }
-
-
- // Destructor (virtual placeholder)
-
- CImageDisplay::~CImageDisplay()
- {
- }
-
-
- // This initialises the format we hold which contains the display device type
- // We do a conversion on the display device type in here so that when we start
- // type checking input formats we can assume that certain fields have been set
- // correctly, an example is when we make the 16 bit mask fields explicit. This
- // is normally called when we receive WM_DEVMODECHANGED device change messages
-
- HRESULT CImageDisplay::RefreshDisplayType()
- {
- CAutoLock cDisplayLock(this);
-
- // Set the preferred format type
-
- ZeroMemory((PVOID)&m_Display,sizeof(VIDEOINFO));
- m_Display.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- m_Display.bmiHeader.biBitCount = FALSE;
-
- // Get the bit depth of a device compatible bitmap
-
- HDC hdcDisplay = GetDC(NULL);
- HBITMAP hbm = CreateCompatibleBitmap(hdcDisplay,1,1);
- GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);
-
- // This call will get the colour table or the proper bitfields
-
- GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);
- DeleteObject(hbm);
- ReleaseDC(NULL,hdcDisplay);
-
- // Complete the display type initialisation
-
- ASSERT(CheckHeaderValidity(&m_Display));
- UpdateFormat(&m_Display);
- return NOERROR;
- }
-
-
- // We assume throughout this code that any bitfields masks are allowed no
- // more than eight bits to store a colour component. This checks that the
- // bit count assumption is enforced and also makes sure that all the bits
- // set are contiguous. We return a boolean TRUE if the field checks out ok
-
- BOOL CImageDisplay::CheckBitFields(const VIDEOINFO *pInput)
- {
- DWORD *pBitFields = (DWORD *) pInput->dwBitMasks;
-
- for (INT iColour = iRED;iColour <= iBLUE;iColour++) {
-
- // First of all work out how many bits are set
-
- DWORD SetBits = CountSetBits(pBitFields[iColour]);
- if (SetBits > iMAXBITS || SetBits == 0) {
- NOTE1("Bit fields for component %d invalid",iColour);
- return FALSE;
- }
-
- // Next work out the number of zero bits prefix
- DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);
-
- // This is going to see if all the bits set are contiguous (as they
- // should be). We know how much to shift them right by from the
- // count of prefix bits. The number of bits set defines a mask, we
- // invert this (ones complement) and AND it with the shifted bit
- // fields. If the result is NON zero then there are bit(s) sticking
- // out the left hand end which means they are not contiguous
-
- DWORD TestField = pBitFields[iColour] >> PrefixBits;
- DWORD Mask = ULONG_MAX << SetBits;
- if (TestField & Mask) {
- NOTE1("Bit fields for component %d not contiguous",iColour);
- return FALSE;
- }
- }
- return TRUE;
- }
-
-
- // This counts the number of bits set in the input field
-
- DWORD CImageDisplay::CountSetBits(const DWORD Field)
- {
- // This is a relatively well known bit counting algorithm
-
- DWORD Count = 0;
- DWORD init = Field;
-
- // Until the input is exhausted, count the number of bits
-
- while (init) {
- init = init & (init - 1); // Turn off the bottommost bit
- Count++;
- }
- return Count;
- }
-
-
- // This counts the number of zero bits upto the first one set NOTE the input
- // field should have been previously checked to ensure there is at least one
- // set although if we don't find one set we return the impossible value 32
-
- DWORD CImageDisplay::CountPrefixBits(const DWORD Field)
- {
- DWORD Mask = 1;
- DWORD Count = 0;
-
- while (TRUE) {
- if (Field & Mask) {
- return Count;
- }
- Count++;
-
- ASSERT(Mask != 0x80000000);
- if (Mask == 0x80000000) {
- return Count;
- }
- Mask <<= 1;
- }
- }
-
-
- // This is called to check the BITMAPINFOHEADER for the input type. There are
- // many implicit dependancies between the fields in a header structure which
- // if we validate now make for easier manipulation in subsequent handling. We
- // also check that the BITMAPINFOHEADER matches it's specification such that
- // fields likes the number of planes is one, that it's structure size is set
- // correctly and that the bitmap dimensions have not been set as negative
-
- BOOL CImageDisplay::CheckHeaderValidity(const VIDEOINFO *pInput)
- {
- CAutoLock cDisplayLock(this);
-
- // Check the bitmap dimensions are not negative
-
- if (pInput->bmiHeader.biWidth <= 0 || pInput->bmiHeader.biHeight <= 0) {
- NOTE("Invalid bitmap dimensions");
- return FALSE;
- }
-
- // Check the compression is either BI_RGB or BI_BITFIELDS
-
- if (pInput->bmiHeader.biCompression != BI_RGB) {
- if (pInput->bmiHeader.biCompression != BI_BITFIELDS) {
- NOTE("Invalid compression format");
- return FALSE;
- }
- }
-
- // If BI_BITFIELDS compression format check the colour depth
-
- if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {
- if (pInput->bmiHeader.biBitCount != 16) {
- if (pInput->bmiHeader.biBitCount != 32) {
- NOTE("BI_BITFIELDS not 16/32 bit depth");
- return FALSE;
- }
- }
- }
-
- // Check the assumptions about the layout of the bit fields
-
- if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {
- if (CheckBitFields(pInput) == FALSE) {
- NOTE("Bit fields are not valid");
- return FALSE;
- }
- }
-
- // Are the number of planes equal to one
-
- if (pInput->bmiHeader.biPlanes != 1) {
- NOTE("Number of planes not one");
- return FALSE;
- }
-
- // Check the image size is consistent (it can be zero)
-
- if (pInput->bmiHeader.biSizeImage != GetBitmapSize(&pInput->bmiHeader)) {
- if (pInput->bmiHeader.biSizeImage) {
- NOTE("Image size incorrectly set");
- return FALSE;
- }
- }
-
- // Check the size of the structure
-
- if (pInput->bmiHeader.biSize != sizeof(BITMAPINFOHEADER)) {
- NOTE("Size of BITMAPINFOHEADER wrong");
- return FALSE;
- }
- return CheckPaletteHeader(pInput);
- }
-
-
- // This runs a few simple tests against the palette fields in the input to
- // see if it looks vaguely correct. The tests look at the number of palette
- // colours present, the number considered important and the biCompression
- // field which should always be BI_RGB as no other formats are meaningful
-
- BOOL CImageDisplay::CheckPaletteHeader(const VIDEOINFO *pInput)
- {
- CAutoLock cDisplayLock(this);
-
- // The checks here are for palettised videos only
-
- if (PALETTISED(pInput) == FALSE) {
- if (pInput->bmiHeader.biClrUsed) {
- NOTE("Invalid palette entries");
- return FALSE;
- }
- return TRUE;
- }
-
- // Compression type of BI_BITFIELDS is meaningless for palette video
-
- if (pInput->bmiHeader.biCompression != BI_RGB) {
- NOTE("Palettised video must be BI_RGB");
- return FALSE;
- }
-
- // Check the number of palette colours is correct
-
- if (pInput->bmiHeader.biClrUsed > PALETTE_ENTRIES(pInput)) {
- NOTE("Too many colours in palette");
- return FALSE;
- }
-
- // The number of important colours shouldn't exceed the number used
-
- if (pInput->bmiHeader.biClrImportant > pInput->bmiHeader.biClrUsed) {
- NOTE("Too many important colours");
- return FALSE;
- }
- return TRUE;
- }
-
-
- // Return the format of the video display
-
- const VIDEOINFO *CImageDisplay::GetDisplayFormat()
- {
- CAutoLock cObjectLock(this);
- return &m_Display;
- }
-
-
- // Return TRUE if the display uses a palette
-
- BOOL CImageDisplay::IsPalettised()
- {
- CAutoLock cDisplayLock(this);
- return PALETTISED(&m_Display);
- }
-
-
- // Return the bit depth of the current display setting
-
- WORD CImageDisplay::GetDisplayDepth()
- {
- CAutoLock cDisplayLock(this);
- return m_Display.bmiHeader.biBitCount;
- }
-
-
- // Initialise the optional fields in a VIDEOINFO. These are mainly to do with
- // the source and destination rectangles and palette information such as the
- // number of colours present. It simplifies our code just a little if we don't
- // have to keep checking for all the different valid permutations in a header
- // every time we want to do anything with it (an example would be creating a
- // palette). We set the base class media type before calling this function so
- // that the media types between the pins match after a connection is made
-
- HRESULT CImageDisplay::UpdateFormat(VIDEOINFO *pVideoInfo)
- {
- ASSERT(pVideoInfo);
-
- BITMAPINFOHEADER *pbmi = HEADER(pVideoInfo);
- SetRectEmpty(&pVideoInfo->rcSource);
- SetRectEmpty(&pVideoInfo->rcTarget);
-
- // Set the number of colours explicitly
-
- if (PALETTISED(pVideoInfo)) {
- if (pVideoInfo->bmiHeader.biClrUsed == 0) {
- pVideoInfo->bmiHeader.biClrUsed = PALETTE_ENTRIES(pVideoInfo);
- }
- }
-
- // The number of important colours shouldn't exceed the number used, on
- // some displays the number of important colours is not initialised when
- // retrieving the display type so we set the colours used correctly
-
- if (pVideoInfo->bmiHeader.biClrImportant > pVideoInfo->bmiHeader.biClrUsed) {
- pVideoInfo->bmiHeader.biClrImportant = PALETTE_ENTRIES(pVideoInfo);
- }
-
- // Change the image size field to be explicit
-
- if (pVideoInfo->bmiHeader.biSizeImage == 0) {
- pVideoInfo->bmiHeader.biSizeImage = GetBitmapSize(&pVideoInfo->bmiHeader);
- }
- return NOERROR;
- }
-
-
- // Lots of video rendering filters want code to check proposed formats are ok
- // This checks the VIDEOINFO we are passed as a media type. If the media type
- // is a valid media type then we return NOERROR otherwise E_INVALIDARG. Note
- // however we only accept formats that can be easily displayed in the display
- // so if we are on a 16 bit device we will not accept 24 bit images. The one
- // complexity is that most displays draw 8 bit palettised images efficiently
- // so if the format is that and the display isn't 16 colour VGA we accept it
-
- HRESULT CImageDisplay::CheckVideoType(const VIDEOINFO *pInput)
- {
- // First of all check the VIDEOINFO looks correct
-
- if (CheckHeaderValidity(pInput) == FALSE) {
- return E_INVALIDARG;
- }
-
- // Virtually all devices support palettised images efficiently
-
- if (m_Display.bmiHeader.biBitCount >= pInput->bmiHeader.biBitCount) {
- if (PALETTISED(pInput) == TRUE) {
- NOTE("(Video) Type connection ACCEPTED");
- return NOERROR;
- }
- }
-
- // Do the colour depths match
-
- if (m_Display.bmiHeader.biBitCount != pInput->bmiHeader.biBitCount) {
- NOTE("Colour depth mismatch");
- return E_INVALIDARG;
- }
-
- // Both input and display formats are either BI_RGB or BI_BITFIELDS
-
- ASSERT(PALETTISED(pInput) == FALSE);
- ASSERT(PALETTISED(&m_Display) == FALSE);
-
- // BI_RGB 16 bit representation is implicitly RGB555, and likewise BI_RGB
- // 24 bit representation is RGB888. So we initialise a pointer to the bit
- // fields they really mean and check against the display device format
-
- const DWORD *pInputMask = GetBitMasks(pInput);
- const DWORD *pDisplayMask = GetBitMasks(&m_Display);
-
- if (pInputMask[iRED] != pDisplayMask[iRED] ||
- pInputMask[iGREEN] != pDisplayMask[iGREEN] ||
- pInputMask[iBLUE] != pDisplayMask[iBLUE]) {
-
- NOTE("(Video) Bit field mismatch");
- return E_INVALIDARG;
- }
-
- NOTE("(Video) Type connection ACCEPTED");
- return NOERROR;
- }
-
-
- // Return the bit masks for the true colour VIDEOINFO provided
-
- const DWORD *CImageDisplay::GetBitMasks(const VIDEOINFO *pVideoInfo)
- {
- static DWORD FailMasks[] = {0,0,0};
-
- if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {
- return pVideoInfo->dwBitMasks;
- }
-
- ASSERT(pVideoInfo->bmiHeader.biCompression == BI_RGB);
-
- switch (pVideoInfo->bmiHeader.biBitCount) {
- case 16: return bits555;
- case 24: return bits888;
- case 32: return bits888;
- default: return FailMasks;
- }
- }
-
-
- // Check to see if we can support media type pmtIn as proposed by the output
- // pin - We first check that the major media type is video and also identify
- // the media sub type. Then we thoroughly check the VIDEOINFO type provided
- // As well as the contained VIDEOINFO being correct the major type must be
- // video, the subtype a recognised video format and the type GUID correct
-
- HRESULT CImageDisplay::CheckMediaType(const CMediaType *pmtIn)
- {
- // Does this have a VIDEOINFO format block
-
- const GUID *pFormatType = pmtIn->FormatType();
- if (*pFormatType != FORMAT_VideoInfo) {
- NOTE("Format GUID not a VIDEOINFO");
- return E_INVALIDARG;
- }
-
- // Check the format looks reasonably ok
-
- ULONG Length = pmtIn->FormatLength();
- if (Length < SIZE_VIDEOHEADER) {
- NOTE("Format smaller than a VIDEOHEADER");
- return E_FAIL;
- }
-
- VIDEOINFO *pInput = (VIDEOINFO *) pmtIn->Format();
-
- // Check the major type is MEDIATYPE_Video
-
- const GUID *pMajorType = pmtIn->Type();
- if (*pMajorType != MEDIATYPE_Video) {
- NOTE("Major type not MEDIATYPE_Video");
- return E_INVALIDARG;
- }
-
- // Check we can identify the media subtype
-
- const GUID *pSubType = pmtIn->Subtype();
- if (GetBitCount(pSubType) == USHRT_MAX) {
- NOTE("Invalid video media subtype");
- return E_INVALIDARG;
- }
- return CheckVideoType(pInput);
- }
-
-
- // Given a video format described by a VIDEOINFO structure we return the mask
- // that is used to obtain the range of acceptable colours for this type, for
- // example, the mask for a 24 bit true colour format is 0xFF in all cases. A
- // 16 bit 5:6:5 display format uses 0xF8, 0xFC and 0xF8, therefore given any
- // RGB triplets we can AND them with these fields to find one that is valid
-
- BOOL CImageDisplay::GetColourMask(DWORD *pMaskRed,
- DWORD *pMaskGreen,
- DWORD *pMaskBlue)
- {
- CAutoLock cDisplayLock(this);
- *pMaskRed = 0xFF;
- *pMaskGreen = 0xFF;
- *pMaskBlue = 0xFF;
-
- // If this format is palettised then it doesn't have bit fields
-
- if (m_Display.bmiHeader.biBitCount < 16) {
- return FALSE;
- }
-
- // If this is a 24 bit true colour display then it can handle all the
- // possible colour component ranges described by a byte. It is never
- // allowed for a 24 bit colour depth image to have BI_BITFIELDS set
-
- if (m_Display.bmiHeader.biBitCount == 24) {
- ASSERT(m_Display.bmiHeader.biCompression == BI_RGB);
- return TRUE;
- }
-
- // Calculate the mask based on the format's bit fields
-
- const DWORD *pBitFields = (DWORD *) GetBitMasks(&m_Display);
- DWORD *pOutputMask[] = { pMaskRed, pMaskGreen, pMaskBlue };
-
- // We know from earlier testing that there are no more than iMAXBITS
- // bits set in the mask and that they are all contiguous. All that
- // therefore remains is to shift them into the correct position
-
- for (INT iColour = iRED;iColour <= iBLUE;iColour++) {
-
- // This works out how many bits there are and where they live
-
- DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);
- DWORD SetBits = CountSetBits(pBitFields[iColour]);
-
- // The first shift moves the bit field so that it is right justified
- // in the DWORD, after which we then shift it back left which then
- // puts the leading bit in the bytes most significant bit position
-
- *(pOutputMask[iColour]) = pBitFields[iColour] >> PrefixBits;
- *(pOutputMask[iColour]) <<= (iMAXBITS - SetBits);
- }
- return TRUE;
- }
-
-
-