winutil.cpp 91 KB

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