Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1227 lines
34 KiB

  1. /******************************************************************************
  2. Copyright (C) Microsoft Corporation 1991-1992. All rights reserved.
  3. Title: avidraw.c - Functions that actually draw video for AVI.
  4. *****************************************************************************/
  5. #include "graphic.h"
  6. //
  7. // if the average key frame spacing is greater than this value, always
  8. // force a buffer.
  9. //
  10. #define KEYFRAME_PANIC_SPACE 2500
  11. #define YIELDATFUNNYTIMES
  12. #define ALIGNULONG(i) ((i+3)&(~3)) /* ULONG aligned ! */
  13. #define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
  14. #define DIBWIDTHBYTES(bi) (DWORD)WIDTHBYTES((int)(bi).biWidth * (int)(bi).biBitCount)
  15. #ifdef WIN32
  16. #define LockCurrentTask(x) (x)
  17. #else
  18. extern FAR PASCAL LockCurrentTask(BOOL);
  19. #endif
  20. BOOL NEAR PASCAL DrawBits(NPMCIGRAPHIC npMCI, DWORD ckid, DWORD cksize, BOOL fHurryUp);
  21. void NEAR PASCAL UpdateDisplayDibPalette(NPMCIGRAPHIC npMCI);
  22. BOOL NEAR PASCAL ProcessPaletteChange(NPMCIGRAPHIC npMCI, DWORD cksize)
  23. {
  24. UINT wStartIndex;
  25. UINT wNumEntries;
  26. UINT w;
  27. LPPALETTEENTRY ppe;
  28. npMCI->dwFlags |= MCIAVI_PALCHANGED;
  29. DPF2(("Setting PALCHANGED\n"));
  30. while (cksize > 4) {
  31. wStartIndex = GET_BYTE();
  32. wNumEntries = GET_BYTE();
  33. /* Skip filler word */
  34. GET_WORD();
  35. /* Zero is used as a shorthand for 256 */
  36. if (wNumEntries == 0)
  37. wNumEntries = 256;
  38. ppe = (LPVOID)npMCI->lp;
  39. for (w=0; w<wNumEntries; w++)
  40. {
  41. npMCI->argb[wStartIndex+w].rgbRed = ppe[w].peRed;
  42. npMCI->argb[wStartIndex+w].rgbGreen = ppe[w].peGreen;
  43. npMCI->argb[wStartIndex+w].rgbBlue = ppe[w].peBlue;
  44. }
  45. SKIP_BYTES(wNumEntries * sizeof(PALETTEENTRY));
  46. cksize -= 4 + wNumEntries * sizeof(PALETTEENTRY);
  47. }
  48. if (npMCI->pbiFormat->biBitCount == 8) {
  49. hmemcpy((LPBYTE) npMCI->pbiFormat + npMCI->pbiFormat->biSize,
  50. (LPBYTE) npMCI->argb,
  51. sizeof(RGBQUAD) * npMCI->pbiFormat->biClrUsed);
  52. }
  53. #ifdef DEBUG
  54. /* Make sure we've used up the entire chunk... */
  55. if (cksize != 0) {
  56. DPF(("MCIAVI: Problem with palc chunk\n"));
  57. }
  58. #endif
  59. return TRUE;
  60. }
  61. /* Display the video from the current record.
  62. */
  63. BOOL NEAR PASCAL DisplayVideoFrame(NPMCIGRAPHIC npMCI, BOOL fHurryUp)
  64. {
  65. DWORD ckid;
  66. DWORD cksize;
  67. BOOL fRet;
  68. int stream;
  69. DWORD dwRet;
  70. LONG len;
  71. DWORD dwDrawStart;
  72. LPVOID lpSave;
  73. LPVOID lpChunk;
  74. /* If we're allowed to skip frames, apply some relatively
  75. ** bogus heuristics to decide if we should do it, and
  76. ** pass the appropriate flag on to the driver.
  77. */
  78. if ((npMCI->lCurrentFrame & 0x0f) == 0) {
  79. fHurryUp = FALSE;
  80. }
  81. /* Even if SKIPFRAMES is off, count how many frames we _would_ have
  82. ** skipped if we could.
  83. */
  84. if (fHurryUp)
  85. ++npMCI->dwSkippedFrames;
  86. if (!(npMCI->dwOptionFlags & MCIAVIO_SKIPFRAMES))
  87. fHurryUp = FALSE;
  88. /* Keep track of what we've drawn. */
  89. npMCI->lFrameDrawn = npMCI->lCurrentFrame;
  90. len = (LONG)npMCI->dwThisRecordSize;
  91. lpSave = npMCI->lp;
  92. /* If it's interleaved, adjust for the next record header.... */
  93. // !!! Only if not last frame?
  94. if (npMCI->wPlaybackAlg == MCIAVI_ALG_INTERLEAVED)
  95. len -= 3 * sizeof(DWORD);
  96. while (len >= 2 * sizeof(DWORD)) {
  97. /* Look at the next chunk */
  98. ckid = GET_DWORD();
  99. cksize = GET_DWORD();
  100. DPF3(("'%.4s': %lu bytes\n", (LPSTR) &ckid, cksize));
  101. if ((LONG) cksize > len) {
  102. AssertSz(FALSE, "Chunk obviously too big!");
  103. break;
  104. }
  105. len -= ((cksize+1)&~1) + 8;
  106. if (len < -1) {
  107. AssertSz(FALSE, "Chunk overflowed what was read in!");
  108. break;
  109. }
  110. lpChunk = npMCI->lp;
  111. stream = StreamFromFOURCC(ckid);
  112. if (stream == npMCI->nVideoStream) {
  113. if ((npMCI->lCurrentFrame < npMCI->lVideoStart) &&
  114. !(npMCI->dwFlags & MCIAVI_REVERSE))
  115. goto skip;
  116. switch(TWOCCFromFOURCC(ckid)) {
  117. case cktypePALchange:
  118. ProcessPaletteChange(npMCI, cksize);
  119. npMCI->lLastPaletteChange = npMCI->lCurrentFrame;
  120. break;
  121. default:
  122. /* Some other chunk... */
  123. if (!fHurryUp && ckid)
  124. dwDrawStart = timeGetTime();
  125. //!!! we need to handle half frames!!!
  126. fRet = DrawBits(npMCI, ckid, cksize, fHurryUp);
  127. if (!fRet)
  128. return FALSE;
  129. if (!fHurryUp && ckid)
  130. npMCI->dwLastDrawTime = timeGetTime() - dwDrawStart;
  131. if (npMCI->dwBufferedVideo)
  132. npMCI->dwLastDrawTime = 0;
  133. break;
  134. }
  135. } else if (stream >= 0 && stream < npMCI->streams &&
  136. SI(stream)->hicDraw) {
  137. dwRet = ICDraw(SI(stream)->hicDraw, (fHurryUp ? ICDRAW_HURRYUP : 0L),
  138. SI(stream)->lpFormat,
  139. (ckid == 0) ? 0L : npMCI->lp, cksize, npMCI->lCurrentFrame);
  140. // !!! Error check?
  141. }
  142. skip:
  143. /* If not interleaved, we're done. */
  144. if (npMCI->wPlaybackAlg != MCIAVI_ALG_INTERLEAVED)
  145. return TRUE;
  146. /* Skip to the next chunk */
  147. npMCI->lp = (HPSTR) lpChunk + ((cksize+1)&~1);
  148. }
  149. npMCI->lp = lpSave;
  150. return TRUE;
  151. }
  152. //
  153. // mark all streams in the passed RECT as dirty
  154. //
  155. void NEAR PASCAL StreamInvalidate(NPMCIGRAPHIC npMCI, LPRECT prc)
  156. {
  157. int i;
  158. int n;
  159. STREAMINFO *psi;
  160. RECT rc;
  161. if (prc)
  162. DPF2(("StreamInvalidate: [%d, %d, %d, %d]\n", *prc));
  163. else
  164. DPF2(("StreamInvalidate: NULL\n", *prc));
  165. for (n=i=0; i<npMCI->streams; i++) {
  166. psi = SI(i);
  167. // we always update any visible error streams
  168. if (!(psi->dwFlags & STREAM_ERROR) &&
  169. !(psi->dwFlags & STREAM_ENABLED))
  170. continue;
  171. if (IsRectEmpty(&psi->rcDest))
  172. continue;
  173. if (prc && !IntersectRect(&rc, prc, &psi->rcDest))
  174. continue;
  175. n++;
  176. psi->dwFlags |= STREAM_NEEDUPDATE;
  177. }
  178. //
  179. // !!!is this right? or should we always dirty the movie?
  180. //
  181. if (n > 0)
  182. npMCI->dwFlags |= MCIAVI_NEEDUPDATE;
  183. else
  184. npMCI->dwFlags &= ~MCIAVI_NEEDUPDATE;
  185. }
  186. //
  187. // update all dirty streams
  188. //
  189. // if fPaint is set paint the area even if the stream handler does not
  190. //
  191. BOOL NEAR PASCAL DoStreamUpdate(NPMCIGRAPHIC npMCI, BOOL fPaint)
  192. {
  193. int i;
  194. BOOL f=TRUE;
  195. STREAMINFO *psi;
  196. Assert(npMCI->hdc);
  197. SaveDC(npMCI->hdc);
  198. for (i=0; i<npMCI->streams; i++) {
  199. psi = SI(i);
  200. //
  201. // this stream is clean, dont paint it.
  202. //
  203. if (!(psi->dwFlags & (STREAM_DIRTY|STREAM_NEEDUPDATE))) {
  204. ExcludeClipRect(npMCI->hdc,
  205. DEST(i).left,DEST(i).top,DEST(i).right,DEST(i).bottom);
  206. continue;
  207. }
  208. psi->dwFlags &= ~STREAM_NEEDUPDATE;
  209. psi->dwFlags &= ~STREAM_DIRTY;
  210. if (psi->dwFlags & STREAM_ERROR) {
  211. UINT u;
  212. TCHAR ach[80];
  213. TCHAR szMessage[80];
  214. HBRUSH hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(128,0,0));
  215. if (psi->sh.fccType == streamtypeVIDEO)
  216. LoadString(ghModule, MCIAVI_CANT_DRAW_VIDEO, ach, sizeof(ach));
  217. else
  218. LoadString(ghModule, MCIAVI_CANT_DRAW_STREAM, ach, sizeof(ach));
  219. FillRect(npMCI->hdc, &DEST(i), hbr);
  220. u = SetBkMode(npMCI->hdc, TRANSPARENT);
  221. wsprintf(szMessage, ach,
  222. (LPVOID)&psi->sh.fccType,
  223. (LPVOID)&psi->sh.fccHandler);
  224. DrawText(npMCI->hdc, szMessage, lstrlen(szMessage), &DEST(i),
  225. DT_WORDBREAK|DT_VCENTER|DT_CENTER);
  226. SetBkMode(npMCI->hdc, u);
  227. DeleteObject(hbr);
  228. FrameRect(npMCI->hdc, &DEST(i), GetStockObject(BLACK_BRUSH));
  229. }
  230. else if (!(psi->dwFlags & STREAM_ENABLED)) {
  231. FillRect(npMCI->hdc, &DEST(i), GetStockObject(DKGRAY_BRUSH));
  232. }
  233. else if (psi->sh.fccType == streamtypeVIDEO &&
  234. !(npMCI->dwFlags & MCIAVI_SHOWVIDEO)) {
  235. continue; // we will paint black here.
  236. }
  237. else if (npMCI->nVideoStreams > 0 && i == npMCI->nVideoStream) {
  238. if (!DrawBits(npMCI, 0L, 0L, FALSE)) {
  239. psi->dwFlags |= STREAM_NEEDUPDATE;
  240. f = FALSE;
  241. if (fPaint) // will paint back if told to.
  242. continue;
  243. }
  244. }
  245. else if (psi->hicDraw == NULL) {
  246. FillRect(npMCI->hdc, &DEST(i), GetStockObject(DKGRAY_BRUSH));
  247. }
  248. else if (ICDraw(psi->hicDraw,ICDRAW_UPDATE,psi->lpFormat,NULL,0,0) != 0) {
  249. psi->dwFlags |= STREAM_NEEDUPDATE;
  250. f = FALSE;
  251. // should other streams work like this?
  252. if (fPaint) // will paint back if told to.
  253. continue;
  254. }
  255. //
  256. // we painted so clean this area
  257. //
  258. ExcludeClipRect(npMCI->hdc,
  259. DEST(i).left,DEST(i).top,DEST(i).right,DEST(i).bottom);
  260. }
  261. // now paint black every where else
  262. FillRect(npMCI->hdc,&npMCI->rcDest,GetStockObject(BLACK_BRUSH));
  263. RestoreDC(npMCI->hdc, -1);
  264. //
  265. // do we still still need a update?
  266. //
  267. if (f) {
  268. npMCI->dwFlags &= ~MCIAVI_NEEDUPDATE;
  269. }
  270. else {
  271. DPF2(("StreamUpdate: update failed\n"));
  272. npMCI->dwFlags |= MCIAVI_NEEDUPDATE;
  273. }
  274. return f;
  275. }
  276. void NEAR PASCAL AlignPlaybackWindow(NPMCIGRAPHIC npMCI)
  277. {
  278. #ifndef WIN32
  279. DWORD dw;
  280. int x,y;
  281. HWND hwnd; // the window we will move.
  282. RECT rc;
  283. // if (npMCI->hicDraw != npMCI->hicDrawInternal)
  284. // return; !!! only align if using the default draw guy?
  285. #pragma message("**** move this into the draw handler and/or DrawDib")
  286. #pragma message("**** we need to query the alignment from the codec????")
  287. #define X_ALIGN 4
  288. #define Y_ALIGN 4
  289. // the MCIAVI_RELEASEDC flags means the DC came from a GetDC(npMCI->hwnd)
  290. if (!(npMCI->dwFlags & MCIAVI_RELEASEDC))
  291. return;
  292. //
  293. // dont align if the dest rect is not at 0,0
  294. //
  295. if (npMCI->rcMovie.left != 0 || npMCI->rcMovie.top != 0)
  296. return;
  297. dw = GetDCOrg(npMCI->hdc);
  298. x = LOWORD(dw) + npMCI->rcMovie.left;
  299. y = HIWORD(dw) + npMCI->rcMovie.top;
  300. if ((x & (X_ALIGN-1)) || (y & (Y_ALIGN-1)))
  301. {
  302. DPF2(("*** warning movie is not aligned! (%d,%d)***\n",x,y));
  303. //
  304. // find the first moveable window walking up the tree.
  305. //
  306. for (hwnd = npMCI->hwnd; hwnd; hwnd = GetParent(hwnd))
  307. {
  308. LONG l = GetWindowLong(hwnd, GWL_STYLE);
  309. // this window is toplevel stop
  310. if (!(l & WS_CHILD))
  311. break;
  312. // this window is sizeable (should be movable too)
  313. if (l & WS_THICKFRAME)
  314. break;
  315. // this window has a caption (is moveable)
  316. if ((l & WS_CAPTION) == WS_CAPTION)
  317. break;
  318. }
  319. //
  320. // dont move the window if it does not want to be moved.
  321. //
  322. if (IsWindowVisible(hwnd) &&
  323. !IsZoomed(hwnd) &&
  324. !IsIconic(hwnd) &&
  325. IsWindowEnabled(hwnd))
  326. {
  327. GetClientRect(hwnd, &rc);
  328. ClientToScreen(hwnd, (LPPOINT)&rc);
  329. //
  330. // if the movie is not in the upper corner of the window
  331. // don't align
  332. //
  333. if (x < rc.left || x-rc.left > 16 ||
  334. y < rc.top || y-rc.top > 16)
  335. return;
  336. GetWindowRect(hwnd, &rc);
  337. OffsetRect(&rc, -(x & (X_ALIGN-1)), -(y & (Y_ALIGN-1)));
  338. if (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
  339. ScreenToClient(GetParent(hwnd), (LPPOINT)&rc);
  340. // dont move window off of the screen.
  341. if (rc.left < 0 || rc.top < 0)
  342. return;
  343. DPF2(("*** moving window to [%d,%d,%d,%d]\n",rc));
  344. SetWindowPos(hwnd,NULL,rc.left,rc.top,0,0,
  345. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
  346. #ifdef DEBUG
  347. dw = GetDCOrg(npMCI->hdc);
  348. x = LOWORD(dw) + npMCI->rcMovie.left;
  349. y = HIWORD(dw) + npMCI->rcMovie.top;
  350. Assert(!(x & (X_ALIGN-1)) && !(y & (Y_ALIGN-1)));
  351. #endif
  352. }
  353. }
  354. #endif
  355. }
  356. UINT NEAR PASCAL PrepareDC(NPMCIGRAPHIC npMCI)
  357. {
  358. UINT u;
  359. int i;
  360. STREAMINFO *psi;
  361. DPF2(("*** PrepareDC(%04X)\n",npMCI->hdc));
  362. Assert(npMCI->hdc != NULL);
  363. if (!(npMCI->dwFlags & MCIAVI_FULLSCREEN) &&
  364. !(npMCI->dwFlags & MCIAVI_SEEKING) &&
  365. !(npMCI->dwFlags & MCIAVI_UPDATING) &&
  366. (npMCI->dwFlags & MCIAVI_SHOWVIDEO) ) {
  367. AlignPlaybackWindow(npMCI);
  368. }
  369. if (npMCI->hicDraw) {
  370. DPF2(("Calling ICDrawRealize\n"));
  371. u = (UINT)ICDrawRealize(npMCI->hicDraw, npMCI->hdc, npMCI->fForceBackground);
  372. } else {
  373. u = 0;
  374. }
  375. //
  376. // realize the other strems, but force them into the background.
  377. //
  378. for (i=0; i<npMCI->streams; i++) {
  379. psi = SI(i);
  380. if (!(psi->dwFlags & STREAM_ENABLED))
  381. continue;
  382. if (psi->dwFlags & STREAM_ERROR)
  383. continue;
  384. if (psi == npMCI->psiVideo)
  385. continue;
  386. if (psi->hicDraw == NULL)
  387. continue;
  388. if (psi->hicDraw == npMCI->hicDraw)
  389. continue;
  390. ICDrawRealize(psi->hicDraw, npMCI->hdc, TRUE);
  391. }
  392. //
  393. // return "master" stream realize value.
  394. //
  395. return u;
  396. }
  397. void NEAR PASCAL UnprepareDC(NPMCIGRAPHIC npMCI)
  398. {
  399. Assert(npMCI->hdc);
  400. DPF2(("*** UnprepareDC(%04X)\n",npMCI->hdc));
  401. SelectPalette(npMCI->hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
  402. RealizePalette(npMCI->hdc);
  403. //RestoreDC(npMCI->hdc, -1);
  404. }
  405. /* This function is called to actually handle drawing.
  406. **
  407. ** ckid and cksize specify the type and size of the data to be drawn;
  408. ** it's located at npMCI->lp.
  409. **
  410. ** If the fHurryUp flag is set, that means that we're behind and we
  411. ** shouldn't draw now. all we do it update the current buffered image
  412. ** and return...
  413. */
  414. BOOL NEAR PASCAL DrawBits(NPMCIGRAPHIC npMCI, DWORD ckid, DWORD cksize, BOOL fHurryUp)
  415. {
  416. LPVOID lp = npMCI->lp;
  417. LPBITMAPINFOHEADER lpFormat = npMCI->pbiFormat;
  418. DWORD dwRet;
  419. DWORD dwFlags;
  420. STREAMINFO *psi;
  421. if (!npMCI->pbiFormat)
  422. return TRUE;
  423. if (npMCI->fNoDrawing || !(npMCI->dwFlags & MCIAVI_SHOWVIDEO))
  424. return TRUE;
  425. psi = SI(npMCI->nVideoStream);
  426. //
  427. // let's compute the flags we need to pass to ICDecompress() and
  428. // to ICDraw()
  429. //
  430. // ICDRAW_HURRYUP - we are behind
  431. // ICDRAW_PREROLL - we are seeking (before a play)
  432. // ICDRAW_UPDATE - update of frame (repaint, ...)
  433. // ICDRAW_NOTKEYFRAME - this frame data is not a key.
  434. //
  435. dwFlags = 0;
  436. if (psi->dwFlags & STREAM_NEEDUPDATE)
  437. dwFlags |= ICDRAW_UPDATE;
  438. if (cksize == 0)
  439. dwFlags |= ICDRAW_NULLFRAME;
  440. if (ckid == 0) {
  441. dwFlags |= ICDRAW_UPDATE;
  442. lp = 0;
  443. }
  444. else if (fHurryUp) {
  445. dwFlags |= ICDRAW_HURRYUP;
  446. psi->dwFlags |= STREAM_DIRTY;
  447. }
  448. else if (!(npMCI->dwFlags & MCIAVI_REVERSE) &&
  449. (npMCI->lCurrentFrame < npMCI->lRealStart)) {
  450. dwFlags |= ICDRAW_PREROLL;
  451. psi->dwFlags |= STREAM_DIRTY;
  452. }
  453. if (npMCI->hpFrameIndex) {
  454. if ((ckid == 0L || cksize == 0) ||
  455. FramePrevKey(npMCI->lCurrentFrame) != npMCI->lCurrentFrame)
  456. dwFlags |= ICDRAW_NOTKEYFRAME;
  457. }
  458. //
  459. // now draw the frame, decompress first if needed.
  460. //
  461. if (npMCI->hic) {
  462. if (ckid != 0L && cksize != 0) {
  463. TIMESTART(timeDecompress);
  464. npMCI->pbiFormat->biSizeImage = cksize; // !!! Is this safe?
  465. dwRet = ICDecompress(npMCI->hic,
  466. dwFlags,
  467. npMCI->pbiFormat,
  468. npMCI->lp,
  469. &npMCI->bih,
  470. npMCI->hpDecompress);
  471. TIMEEND(timeDecompress);
  472. if (dwRet == ICERR_DONTDRAW) {
  473. return TRUE; // !!!???
  474. }
  475. // ICERR_NEWPALETTE?
  476. dwFlags &= (~ICDRAW_NOTKEYFRAME); // It's a key frame now....
  477. }
  478. if (dwFlags & (ICDRAW_HURRYUP|ICDRAW_PREROLL))
  479. return TRUE;
  480. lpFormat = &npMCI->bih;
  481. lp = npMCI->hpDecompress;
  482. cksize = npMCI->bih.biSizeImage;
  483. }
  484. TIMESTART(timeDraw);
  485. if ((npMCI->dwFlags & MCIAVI_PALCHANGED) &&
  486. !(dwFlags & (ICDRAW_HURRYUP|ICDRAW_PREROLL))) {
  487. if (psi->ps) {
  488. if (npMCI->hic) {
  489. //!!! should be psi->lpFormat *not* npMCI->pbiFormat
  490. ICDecompressGetPalette(npMCI->hic, npMCI->pbiFormat, &npMCI->bih);
  491. ICDrawChangePalette(npMCI->hicDraw, &npMCI->bih);
  492. }
  493. else {
  494. ICDrawChangePalette(npMCI->hicDraw, npMCI->pbiFormat);
  495. }
  496. }
  497. else {
  498. DPF2(("Calling ICDrawChangePalette\n"));
  499. ICDrawChangePalette(npMCI->hicDraw, &npMCI->bih);
  500. }
  501. npMCI->dwFlags &= ~(MCIAVI_PALCHANGED);
  502. dwFlags &= ~ICDRAW_HURRYUP; // should realy draw this!
  503. }
  504. if ((npMCI->dwFlags & MCIAVI_SEEKING) &&
  505. !(dwFlags & ICDRAW_PREROLL))
  506. PrepareDC(npMCI);
  507. lpFormat->biSizeImage = cksize; // !!! ??? Is this safe?
  508. //
  509. // !!!do we realy realy want to do this here?
  510. // or just relay on the MPlay(er) status function
  511. //
  512. ////if (npMCI->dwFlags & MCIAVI_WANTMOVE)
  513. //// CheckWindowMoveFast(npMCI);
  514. DPF3(("Calling ICDraw on frame %ld (%08lx)\n", npMCI->lCurrentFrame, dwFlags));
  515. dwRet = ICDraw(npMCI->hicDraw, dwFlags, lpFormat, lp, cksize,
  516. npMCI->lCurrentFrame - npMCI->lFramePlayStart);
  517. TIMEEND(timeDraw);
  518. if ((LONG) dwRet < ICERR_OK) {
  519. DPF(("Driver failed ICM_DRAW message err=%ld\n", dwRet));
  520. return FALSE;
  521. }
  522. else {
  523. psi->dwFlags &= ~STREAM_NEEDUPDATE;
  524. if (!(dwFlags & (ICDRAW_HURRYUP|ICDRAW_PREROLL)))
  525. psi->dwFlags &= ~STREAM_DIRTY;
  526. }
  527. return TRUE;
  528. }
  529. /***************************************************************************
  530. ***************************************************************************/
  531. static void FreeDecompressBuffer(NPMCIGRAPHIC npMCI)
  532. {
  533. if (npMCI->hpDecompress)
  534. GlobalFreePtr(npMCI->hpDecompress);
  535. npMCI->hpDecompress = NULL;
  536. npMCI->cbDecompress = 0;
  537. }
  538. /***************************************************************************
  539. ***************************************************************************/
  540. static BOOL GetDecompressBuffer(NPMCIGRAPHIC npMCI)
  541. {
  542. int n = npMCI->nVideoStream;
  543. int dxDest = RCW(DEST(n));
  544. int dyDest = RCH(DEST(n));
  545. HPSTR hp;
  546. npMCI->bih.biSizeImage = npMCI->bih.biHeight * DIBWIDTHBYTES(npMCI->bih);
  547. if ((LONG) npMCI->bih.biSizeImage <= npMCI->cbDecompress)
  548. return TRUE;
  549. if (!npMCI->hpDecompress)
  550. hp = GlobalAllocPtr(GHND|GMEM_SHARE, npMCI->bih.biSizeImage);
  551. else
  552. hp = GlobalReAllocPtr(npMCI->hpDecompress,
  553. npMCI->bih.biSizeImage,
  554. GMEM_MOVEABLE | GMEM_SHARE);
  555. if (hp == NULL) {
  556. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  557. return FALSE;
  558. }
  559. npMCI->hpDecompress = hp;
  560. npMCI->cbDecompress = npMCI->bih.biSizeImage;
  561. return TRUE;
  562. }
  563. /*
  564. Possibilities:
  565. 1. We're starting to play.
  566. We may need to switch into fullscreen mode.
  567. We need a DrawBegin.
  568. 2. We're updating the screen.
  569. Do we send a new DrawBegin?
  570. Has anything changed since we last updated? Perhaps we can use
  571. a flag to say whether something has changed, and set it when we leave
  572. fullscreen mode or when the window is stretched.
  573. What if we're updating to memory?
  574. 3. We're playing, and the user has stretched the window.
  575. The Draw device may need us to go back to a key frame.
  576. If we have a separate decompressor, it may need us to go back to a key frame.
  577. */
  578. #if 0
  579. RestartCompressor()
  580. {
  581. DWORD dwDrawFlags;
  582. dwDrawFlags = (npMCI->dwFlags & MCIAVI_FULLSCREEN) ?
  583. ICDRAW_FULLSCREEN : ICDRAW_HDC;
  584. if (pfRestart)
  585. dwDrawFlags |= ICDRAW_CONTINUE;
  586. if (npMCI->dwFlags & MCIAVI_UPDATETOMEMORY)
  587. dwDrawFlags |= ICDRAW_MEMORYDC;
  588. if (npMCI->hic) {
  589. static struct {
  590. BITMAPINFOHEADER bi;
  591. RGBQUAD rgbq[256];
  592. } dib;
  593. }
  594. }
  595. #endif
  596. BOOL TryDrawDevice(NPMCIGRAPHIC npMCI, HIC hicDraw, DWORD dwDrawFlags, BOOL fTryDecompress)
  597. {
  598. DWORD dw;
  599. int n = npMCI->nVideoStream;
  600. STREAMINFO *psi = SI(n);
  601. Assert(psi);
  602. if (hicDraw == NULL)
  603. return FALSE;
  604. // See if the standard draw device can handle the format
  605. dw = ICDrawBegin(hicDraw,
  606. dwDrawFlags,
  607. npMCI->hpal, // palette to draw with
  608. npMCI->hwnd, // window to draw to
  609. npMCI->hdc, // HDC to draw to
  610. RCX(DEST(n)),
  611. RCY(DEST(n)),
  612. RCW(DEST(n)),
  613. RCH(DEST(n)),
  614. npMCI->pbiFormat,
  615. RCX(SOURCE(n)),
  616. RCY(SOURCE(n)),
  617. RCW(SOURCE(n)),
  618. RCH(SOURCE(n)),
  619. // !!! First of all, these two are backwards.
  620. // !!! Secondly, what if PlayuSec == 0?
  621. npMCI->dwPlayMicroSecPerFrame,
  622. 1000000L);
  623. if (dw == ICERR_OK) {
  624. npMCI->hic = 0;
  625. npMCI->hicDraw = hicDraw;
  626. return TRUE;
  627. }
  628. if (npMCI->hicDecompress && fTryDecompress) {
  629. RECT rc;
  630. // Ask the draw device to suggest a format, then try to get our
  631. // decompressor to make that format.
  632. dw = ICDrawSuggestFormat(hicDraw,
  633. npMCI->pbiFormat,
  634. &npMCI->bih,
  635. RCW(SOURCE(n)),
  636. RCH(SOURCE(n)),
  637. RCW(DEST(n)),
  638. RCH(DEST(n)),
  639. npMCI->hicDecompress);
  640. if ((LONG)dw >= 0)
  641. dw = ICDecompressQuery(npMCI->hicDecompress,
  642. npMCI->pbiFormat,&npMCI->bih);
  643. if ((LONG)dw < 0) {
  644. //
  645. // default to the right format for the screen, in case the draw guy
  646. // fails the draw suggest.
  647. //
  648. ICGetDisplayFormat(npMCI->hicDecompress,
  649. npMCI->pbiFormat,&npMCI->bih, 0,
  650. MulDiv((int)npMCI->pbiFormat->biWidth, RCW(psi->rcDest),RCW(psi->rcSource)),
  651. MulDiv((int)npMCI->pbiFormat->biHeight,RCH(psi->rcDest),RCH(psi->rcSource)));
  652. dw = ICDecompressQuery(npMCI->hicDecompress,
  653. npMCI->pbiFormat,&npMCI->bih);
  654. if (dw != ICERR_OK) {
  655. npMCI->dwTaskError = MCIERR_INTERNAL;
  656. return FALSE;
  657. }
  658. }
  659. if (npMCI->bih.biBitCount <= 8) {
  660. ICDecompressGetPalette(npMCI->hicDecompress,
  661. npMCI->pbiFormat, &npMCI->bih);
  662. }
  663. #ifdef DEBUG
  664. DPF(("InitDecompress: Decompressing %dx%dx%d '%4.4ls' to %dx%dx%d\n",
  665. (int)npMCI->pbiFormat->biWidth,
  666. (int)npMCI->pbiFormat->biHeight,
  667. (int)npMCI->pbiFormat->biBitCount,
  668. (LPSTR)(
  669. npMCI->pbiFormat->biCompression == BI_RGB ? "None" :
  670. npMCI->pbiFormat->biCompression == BI_RLE8 ? "Rle8" :
  671. npMCI->pbiFormat->biCompression == BI_RLE4 ? "Rle4" :
  672. (LPSTR)&npMCI->pbiFormat->biCompression),
  673. (int)npMCI->bih.biWidth,
  674. (int)npMCI->bih.biHeight,
  675. (int)npMCI->bih.biBitCount));
  676. #endif
  677. if (!GetDecompressBuffer(npMCI))
  678. return FALSE;
  679. //
  680. // setup the "real" source rect we will draw with.
  681. //
  682. #if 0
  683. rc.left = (int) ((SOURCE(n).left * npMCI->bih.biWidth) / npMCI->pbiFormat->biWidth);
  684. rc.right = (int) ((SOURCE(n).right * npMCI->bih.biWidth) / npMCI->pbiFormat->biWidth);
  685. rc.top = (int) ((SOURCE(n).top * npMCI->bih.biHeight) / npMCI->pbiFormat->biHeight);
  686. rc.bottom = (int) ((SOURCE(n).bottom * npMCI->bih.biHeight) / npMCI->pbiFormat->biHeight);
  687. #else
  688. rc = SOURCE(n);
  689. rc.left = (int) ((rc.left * npMCI->bih.biWidth) / npMCI->pbiFormat->biWidth);
  690. rc.right = (int) ((rc.right * npMCI->bih.biWidth) / npMCI->pbiFormat->biWidth);
  691. rc.top = (int) ((rc.top * npMCI->bih.biHeight) / npMCI->pbiFormat->biHeight);
  692. rc.bottom = (int) ((rc.bottom * npMCI->bih.biHeight) / npMCI->pbiFormat->biHeight);
  693. #endif
  694. dw = ICDrawBegin(hicDraw,
  695. dwDrawFlags,
  696. npMCI->hpal, // palette to draw with
  697. npMCI->hwnd, // window to draw to
  698. npMCI->hdc, // HDC to draw to
  699. RCX(DEST(n)),
  700. RCY(DEST(n)),
  701. RCW(DEST(n)),
  702. RCH(DEST(n)),
  703. &npMCI->bih,
  704. rc.left, rc.top,
  705. rc.right - rc.left,
  706. rc.bottom - rc.top,
  707. // !!! First of all, these two are backwards.
  708. // !!! Secondly, what if PlayuSec == 0?
  709. npMCI->dwPlayMicroSecPerFrame,
  710. 1000000L);
  711. if (dw == ICERR_OK) {
  712. npMCI->hic = npMCI->hicDecompress;
  713. npMCI->hicDraw = hicDraw;
  714. // Now, we have the format we'd like the decompressor to decompress to...
  715. dw = ICDecompressBegin(npMCI->hicDecompress,
  716. npMCI->pbiFormat,
  717. &npMCI->bih);
  718. if (dw != ICERR_OK) {
  719. DPF(("DrawBegin: decompressor succeeded query, failed begin!\n"));
  720. ICDrawEnd(npMCI->hicDraw);
  721. return FALSE;
  722. }
  723. return TRUE;
  724. }
  725. if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
  726. npMCI->dwTaskError = MCIERR_AVI_NODISPDIB;
  727. }
  728. }
  729. return FALSE;
  730. }
  731. BOOL FindDrawDevice(NPMCIGRAPHIC npMCI, DWORD dwDrawFlags)
  732. {
  733. if (npMCI->dwFlags & MCIAVI_USERDRAWPROC) {
  734. // If the user has set a draw procedure, try it.
  735. if (TryDrawDevice(npMCI, npMCI->hicDrawDefault, dwDrawFlags, TRUE)) {
  736. if (npMCI->hic) {
  737. DPF2(("Using decompressor, then application's draw device...\n"));
  738. } else {
  739. DPF2(("Using application's draw device...\n"));
  740. }
  741. return TRUE;
  742. }
  743. // If it fails, it fails.
  744. DPF(("Can't use application's draw device!\n"));
  745. return FALSE;
  746. }
  747. // First, try a pure draw device we've found.
  748. if (TryDrawDevice(npMCI, SI(npMCI->nVideoStream)->hicDraw, dwDrawFlags, FALSE)) {
  749. DPF2(("Draw device is drawing to the screen...\n"));
  750. return TRUE;
  751. }
  752. // Next, try see if the decompressor we found can draw too.
  753. // Should this even get asked before the guy above?!!!!
  754. if (TryDrawDevice(npMCI, npMCI->hicDecompress, dwDrawFlags, FALSE)) {
  755. DPF2(("Decompressor is drawing to the screen...\n"));
  756. return TRUE;
  757. }
  758. // No? Then, get the standard draw device, for fullscreen or not.
  759. if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
  760. // !!! If it's fullscreen, should we force a re-begin?
  761. // !!! Assume fullscreen only happens when play is starting?
  762. if (npMCI->hicDrawFull == NULL) {
  763. DPF2(("Opening default fullscreen codec...\n"));
  764. npMCI->hicDrawFull = ICOpen(streamtypeVIDEO,
  765. FOURCC_AVIFull,ICMODE_DRAW);
  766. if (!npMCI->hicDrawFull)
  767. npMCI->hicDrawFull = (HIC) -1;
  768. }
  769. npMCI->hicDraw = npMCI->hicDrawFull;
  770. } else {
  771. if (npMCI->hicDrawDefault == NULL) {
  772. DPF2(("Opening default draw codec...\n"));
  773. npMCI->hicDrawDefault = ICOpen(streamtypeVIDEO,
  774. FOURCC_AVIDraw,ICMODE_DRAW);
  775. if (!npMCI->hicDrawDefault)
  776. npMCI->hicDrawDefault = (HIC) -1;
  777. }
  778. npMCI->hicDraw = npMCI->hicDrawDefault;
  779. }
  780. // If there's an installed draw device, try it.
  781. if (npMCI->hicDraw && npMCI->hicDraw != (HIC) -1) {
  782. if (TryDrawDevice(npMCI, npMCI->hicDraw, dwDrawFlags, TRUE)) {
  783. if (npMCI->hic) {
  784. DPF2(("Using decompressor, then default draw device...\n"));
  785. } else {
  786. DPF2(("Using default draw device...\n"));
  787. }
  788. return TRUE;
  789. }
  790. }
  791. if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
  792. if (!npMCI->hicInternalFull)
  793. npMCI->hicInternalFull = ICOpenFunction(streamtypeVIDEO,
  794. FOURCC_AVIFull,ICMODE_DRAW,(FARPROC)ICAVIFullProc);
  795. npMCI->hicDraw = npMCI->hicInternalFull;
  796. } else {
  797. if (!npMCI->hicInternal) {
  798. npMCI->hicInternal = ICOpenFunction(streamtypeVIDEO,
  799. FOURCC_AVIDraw,ICMODE_DRAW,(FARPROC)ICAVIDrawProc);
  800. #ifdef DEBUG
  801. {
  802. extern HANDLE ghdd;
  803. npMCI->hdd = ghdd;
  804. ghdd = NULL;
  805. }
  806. #endif
  807. }
  808. npMCI->hicDraw = npMCI->hicInternal;
  809. }
  810. // As a last resort, try the built-in draw device.
  811. if (TryDrawDevice(npMCI, npMCI->hicDraw, dwDrawFlags, TRUE)) {
  812. if (npMCI->hic) {
  813. DPF2(("Using decompressor, then built-in draw device...\n"));
  814. } else {
  815. DPF2(("Using built-in draw device...\n"));
  816. }
  817. return TRUE;
  818. }
  819. return FALSE;
  820. }
  821. /**************************************************************************
  822. * @doc INTERNAL DRAWDIB
  823. *
  824. * @api BOOL | DibEq | This function compares two dibs.
  825. *
  826. * @parm LPBITMAPINFOHEADER lpbi1 | Pointer to one bitmap.
  827. * this DIB is assumed to have the colors after the BITMAPINFOHEADER
  828. *
  829. * @parm LPBITMAPINFOHEADER | lpbi2 | Pointer to second bitmap.
  830. * this DIB is assumed to have the colors after biSize bytes.
  831. *
  832. * @rdesc Returns TRUE if bitmaps are identical, FALSE otherwise.
  833. *
  834. **************************************************************************/
  835. BOOL DibEq(LPBITMAPINFOHEADER lpbi1, LPBITMAPINFOHEADER lpbi2)
  836. {
  837. return
  838. lpbi1->biCompression == lpbi2->biCompression &&
  839. lpbi1->biSize == lpbi2->biSize &&
  840. lpbi1->biWidth == lpbi2->biWidth &&
  841. lpbi1->biHeight == lpbi2->biHeight &&
  842. lpbi1->biBitCount == lpbi2->biBitCount;
  843. }
  844. /***************************************************************************
  845. *
  846. * @doc INTERNAL MCIAVI
  847. *
  848. * @api void | DrawBegin
  849. *
  850. *
  851. ***************************************************************************/
  852. BOOL FAR PASCAL DrawBegin(NPMCIGRAPHIC npMCI, BOOL FAR *pfRestart)
  853. {
  854. DWORD dwDrawFlags;
  855. HIC hicLast = npMCI->hic;
  856. HIC hicLastDraw = npMCI->hicDraw;
  857. BITMAPINFOHEADER bihDecompLast = npMCI->bih;
  858. if (npMCI->nVideoStreams == 0)
  859. return TRUE;
  860. if (!npMCI->pbiFormat)
  861. return TRUE;
  862. // if fullscreen, make sure we re-initialize....
  863. if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
  864. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  865. }
  866. npMCI->fNoDrawing = FALSE;
  867. dwDrawFlags = (npMCI->dwFlags & MCIAVI_FULLSCREEN) ?
  868. ICDRAW_FULLSCREEN : ICDRAW_HDC;
  869. if (pfRestart) {
  870. dwDrawFlags |= ICDRAW_CONTINUE;
  871. *pfRestart = TRUE;
  872. }
  873. if (npMCI->dwFlags & MCIAVI_UPDATETOMEMORY)
  874. dwDrawFlags |= ICDRAW_MEMORYDC;
  875. // !!! What about easy mode?
  876. //
  877. // if the file has no keyframes force a buffer
  878. //
  879. if (npMCI->dwKeyFrameInfo == 0)
  880. dwDrawFlags |= ICDRAW_BUFFER;
  881. //
  882. // if the file has few keyframes also force a buffer.
  883. //
  884. if (MovieToTime(npMCI->dwKeyFrameInfo) > KEYFRAME_PANIC_SPACE)
  885. dwDrawFlags |= ICDRAW_BUFFER;
  886. if (dwDrawFlags & ICDRAW_BUFFER)
  887. DPF(("Forcing a decompress buffer because too few key frames\n"));
  888. if (npMCI->wTaskState > TASKIDLE &&
  889. !(npMCI->dwFlags & MCIAVI_SEEKING) &&
  890. !(npMCI->dwFlags & MCIAVI_FULLSCREEN) &&
  891. (npMCI->dwFlags & MCIAVI_ANIMATEPALETTE)) {
  892. dwDrawFlags |= ICDRAW_ANIMATE;
  893. #if 0
  894. //
  895. // I moved all this into ShowStage() where you could claim it realy belongs.
  896. //
  897. if (npMCI->hwnd == npMCI->hwndDefault &&
  898. !(GetWindowLong(npMCI->hwnd, GWL_STYLE) & WS_CHILD))
  899. SetActiveWindow(npMCI->hwnd);
  900. #endif
  901. }
  902. if (npMCI->hdc == NULL) {
  903. DPF2(("DrawBegin() with NULL hdc!\n"));
  904. }
  905. if (FindDrawDevice(npMCI, dwDrawFlags)) {
  906. if (npMCI->hicDraw != hicLastDraw || (npMCI->hic != hicLast) ||
  907. (npMCI->hic && !DibEq(&npMCI->bih, &bihDecompLast))) {
  908. // !!! This obviously shouldn't always be invalidated!
  909. //
  910. // make sure the, current image buffer is invalidated
  911. //
  912. DPF2(("Draw device is different; restarting....\n"));
  913. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  914. npMCI->dwFlags |= MCIAVI_WANTMOVE;
  915. if (pfRestart)
  916. *pfRestart = TRUE;
  917. }
  918. if (npMCI->dwFlags & MCIAVI_WANTMOVE)
  919. CheckWindowMove(npMCI, TRUE);
  920. // if (pfRestart)
  921. // *pfRestart = (dw == ICERR_GOTOKEYFRAME);
  922. npMCI->dwFlags &= ~(MCIAVI_NEEDDRAWBEGIN);
  923. #if 0
  924. //
  925. // tell the compressor some interesting info.
  926. //
  927. if (npMCI->hicDraw) { // !!! Does npMCI->hic need to know this?
  928. ICSendMessage(npMCI->hic, ICM_SET, ICM_FRAMERATE, npMCI->dwPlayMicroSecPerFrame);
  929. ICSendMessage(npMCI->hic, ICM_SET, ICM_KEYFRAMERATE, npMCI->dwKeyFrameInfo);
  930. }
  931. #endif
  932. return TRUE;
  933. }
  934. return FALSE;
  935. }
  936. /***************************************************************************
  937. *
  938. * @doc INTERNAL MCIAVI
  939. *
  940. * @api void | DrawEnd
  941. *
  942. * @parm NPMCIGRAPHIC | npMCI | pointer to instance data block.
  943. *
  944. ***************************************************************************/
  945. void NEAR PASCAL DrawEnd(NPMCIGRAPHIC npMCI)
  946. {
  947. if (!npMCI->pbiFormat)
  948. return;
  949. ICDrawEnd(npMCI->hicDraw);
  950. // if we were fullscreen, we now need to repaint and things....
  951. if (npMCI->dwFlags & MCIAVI_FULLSCREEN) {
  952. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  953. }
  954. /*
  955. ** let DrawDib clean up if we're animating the palette.
  956. */
  957. if (npMCI->wTaskState > TASKIDLE &&
  958. !(npMCI->dwFlags & MCIAVI_SEEKING) &&
  959. !(npMCI->dwFlags & MCIAVI_FULLSCREEN) &&
  960. !(npMCI->dwFlags & MCIAVI_UPDATING) &&
  961. (npMCI->dwFlags & MCIAVI_ANIMATEPALETTE)) {
  962. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  963. InvalidateRect(npMCI->hwnd, NULL, FALSE);
  964. }
  965. }