Source code of Windows XP (NT5)
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.

658 lines
18 KiB

  1. /****************************************************************************
  2. *
  3. * cappal.c
  4. *
  5. * Palette processing module.
  6. *
  7. * Microsoft Video for Windows Sample Capture Class
  8. *
  9. * Copyright (c) 1992, 1993 Microsoft Corporation. All Rights Reserved.
  10. *
  11. * You have a royalty-free right to use, modify, reproduce and
  12. * distribute the Sample Files (and/or any modified version) in
  13. * any way you find useful, provided that you agree that
  14. * Microsoft has no warranty obligations or liability for any
  15. * Sample Application Files which are modified.
  16. *
  17. ***************************************************************************/
  18. #include <windows.h>
  19. #include <windowsx.h>
  20. #include <mmsystem.h>
  21. #include <msvideo.h>
  22. #include <drawdib.h>
  23. #include "avicap.h"
  24. #include "avicapi.h"
  25. #include "cappal.h"
  26. #include "capdib.h"
  27. #include "dibmap.h"
  28. //
  29. // Allocate and initialize palette resources at Window create time
  30. //
  31. BOOL PalInit (LPCAPSTREAM lpcs)
  32. {
  33. return (PalGetPaletteFromDriver (lpcs));
  34. }
  35. //
  36. // Release palette resources at Window destroy time
  37. //
  38. void PalFini (LPCAPSTREAM lpcs)
  39. {
  40. PalDeleteCurrentPalette (lpcs);
  41. }
  42. //
  43. // Delete our palette if it isn't the system default palette
  44. //
  45. void PalDeleteCurrentPalette (LPCAPSTREAM lpcs)
  46. {
  47. if (lpcs->hPalCurrent &&
  48. (lpcs->hPalCurrent != GetStockObject(DEFAULT_PALETTE)))
  49. DeleteObject (lpcs->hPalCurrent);
  50. lpcs->hPalCurrent = NULL;
  51. }
  52. //
  53. // Get the current palette (from the driver)
  54. // Returns: TRUE if the driver can supply a palette
  55. //
  56. BOOL PalGetPaletteFromDriver (LPCAPSTREAM lpcs)
  57. {
  58. FCLOGPALETTE pal;
  59. PalDeleteCurrentPalette (lpcs);
  60. pal.palVersion = 0x0300;
  61. pal.palNumEntries = 256;
  62. lpcs->sCapDrvCaps.fDriverSuppliesPalettes = FALSE; // assume the worst
  63. if (lpcs->fHardwareConnected) {
  64. if (videoConfigure (lpcs->hVideoIn,
  65. DVM_PALETTE,
  66. VIDEO_CONFIGURE_GET | VIDEO_CONFIGURE_CURRENT, NULL,
  67. (LPVOID)&pal, sizeof(pal),
  68. NULL, NULL ) == DV_ERR_OK) {
  69. if (lpcs->hPalCurrent = CreatePalette ((LPLOGPALETTE) &pal))
  70. lpcs->sCapDrvCaps.fDriverSuppliesPalettes = TRUE;
  71. }
  72. }
  73. if (!lpcs->hPalCurrent)
  74. lpcs->hPalCurrent = GetStockObject (DEFAULT_PALETTE);
  75. DibNewPalette (lpcs, lpcs->hPalCurrent);
  76. return (lpcs->sCapDrvCaps.fDriverSuppliesPalettes);
  77. }
  78. //
  79. // Set the current palette used for capture by sending a copy to the driver
  80. // and then copying the entries to out DIB.
  81. // Returns DV_ERR_OK on success, or DV_ERR... on failure.
  82. //
  83. DWORD PalSendPaletteToDriver (LPCAPSTREAM lpcs, HPALETTE hpal, LPBYTE lpXlateTable)
  84. {
  85. int nColors;
  86. FCLOGPALETTE pal;
  87. HCURSOR hOldCursor;
  88. // The following can take a while so repaint our parent
  89. UpdateWindow (GetParent (lpcs-> hwnd));
  90. UpdateWindow (lpcs->hwnd);
  91. if (!hpal)
  92. return FALSE;
  93. PalDeleteCurrentPalette (lpcs);
  94. lpcs->hPalCurrent = hpal;
  95. GetObject(hpal, sizeof(int), (LPVOID)&nColors);
  96. if( nColors <= 1 ) { //!!>
  97. return( FALSE );
  98. }
  99. if (nColors > 256) //???
  100. ;
  101. nColors = min(256, nColors);
  102. hOldCursor = SetCursor (lpcs-> hWaitCursor);
  103. statusUpdateStatus (lpcs, IDS_CAP_STAT_PALETTE_BUILD);
  104. pal.palVersion = 0x0300;
  105. pal.palNumEntries = nColors;
  106. GetPaletteEntries(hpal, 0, nColors, pal.palPalEntry);
  107. if (lpcs-> fHardwareConnected) {
  108. // first try to send both the xlate table and the palette
  109. if ((!lpXlateTable) || (videoConfigure( lpcs->hVideoIn,
  110. DVM_PALETTERGB555,
  111. VIDEO_CONFIGURE_SET, NULL,
  112. (LPLOGPALETTE)&pal, sizeof(pal),
  113. lpXlateTable, (DWORD) 0x8000) != 0)) {
  114. // else send just the palette and make the driver build the table
  115. videoConfigure( lpcs->hVideoIn,
  116. DVM_PALETTE,
  117. VIDEO_CONFIGURE_SET, NULL,
  118. (LPLOGPALETTE)&pal, sizeof(pal),
  119. NULL, NULL );
  120. }
  121. }
  122. // Supermac wants us to get the palette again, they might have
  123. // mucked with it!
  124. PalGetPaletteFromDriver (lpcs);
  125. // Since the palette has changed, delete any existing compression
  126. // output format; this forces a new output format to be selected
  127. if (lpcs->CompVars.lpbiOut) {
  128. GlobalFreePtr (lpcs->CompVars.lpbiOut);
  129. lpcs->CompVars.lpbiOut = NULL;
  130. }
  131. if (lpcs->CompVars.hic) {
  132. if (ICSeqCompressFrameStart(&lpcs->CompVars, lpcs->lpBitsInfo) == NULL) {
  133. lpcs-> dwReturn = IDS_CAP_COMPRESSOR_ERROR;
  134. errorUpdateError (lpcs, IDS_CAP_COMPRESSOR_ERROR);
  135. }
  136. }
  137. InvalidateRect (lpcs->hwnd, NULL, TRUE);
  138. UpdateWindow (lpcs->hwnd);
  139. SetCursor (hOldCursor);
  140. statusUpdateStatus (lpcs, NULL);
  141. return (TRUE);
  142. }
  143. //
  144. // CopyPalette, makes a copy of a GDI logical palette
  145. // Returns: a handle to the newly created palette, or NULL if error
  146. //
  147. HPALETTE CopyPalette (HPALETTE hpal)
  148. {
  149. LPLOGPALETTE lppal;
  150. int nNumEntries;
  151. if (!hpal)
  152. return NULL;
  153. GetObject (hpal,sizeof(int),(LPVOID)&nNumEntries);
  154. if (nNumEntries == 0)
  155. return NULL;
  156. lppal = (LPLOGPALETTE) GlobalAllocPtr (GHND,
  157. sizeof(LOGPALETTE) + nNumEntries * sizeof(PALETTEENTRY));
  158. if (!lppal)
  159. return NULL;
  160. lppal->palVersion = 0x300;
  161. lppal->palNumEntries = nNumEntries;
  162. GetPaletteEntries(hpal,0,nNumEntries,lppal->palPalEntry);
  163. hpal = CreatePalette(lppal);
  164. GlobalFreePtr (lppal);
  165. return hpal;
  166. }
  167. //
  168. // Allocate resources needed for palette capture
  169. // Returns DV_ERR_OK on success, or DV_ERR... on failure.
  170. // Note: if Init fails, you MUST call the Fini function to
  171. // release resources.
  172. //
  173. DWORD CapturePaletteInit (LPCAPSTREAM lpcs, LPCAPPAL lpcp)
  174. {
  175. DWORD dwError = DV_ERR_OK;
  176. lpcp->lpBits = NULL;
  177. lpcp->lp16to8 = NULL;
  178. lpcp->lpHistogram = NULL;
  179. lpcp->lpbiSave = NULL;
  180. lpcp->wNumFrames = 0;
  181. // Init an RGB16 header
  182. lpcp->bi16.biSize = sizeof(BITMAPINFOHEADER);
  183. lpcp->bi16.biWidth = lpcs->dxBits;
  184. lpcp->bi16.biHeight = lpcs->dyBits;
  185. lpcp->bi16.biPlanes = 1;
  186. lpcp->bi16.biBitCount = 16;
  187. lpcp->bi16.biCompression = BI_RGB;
  188. lpcp->bi16.biSizeImage = DIBWIDTHBYTES(lpcp->bi16) * lpcp->bi16.biHeight;
  189. lpcp->bi16.biXPelsPerMeter= 0;
  190. lpcp->bi16.biYPelsPerMeter= 0;
  191. lpcp->bi16.biClrUsed = 0;
  192. lpcp->bi16.biClrImportant = 0;
  193. // Allocate memory for the histogram, DIB, and XLate table
  194. lpcp->lpBits = GlobalAllocPtr (GHND, lpcp->bi16.biSizeImage);
  195. lpcp->lp16to8 = GlobalAllocPtr (GHND, 0x8000l);
  196. lpcp->lpHistogram = InitHistogram(NULL);
  197. if (!lpcp->lpBits || !lpcp->lp16to8 || !lpcp->lpHistogram) {
  198. dwError = DV_ERR_NOMEM;
  199. goto PalInitError;
  200. }
  201. // Init the video header
  202. lpcp->vHdr.lpData = lpcp->lpBits;
  203. lpcp->vHdr.dwBufferLength = lpcp->bi16.biSizeImage;
  204. lpcp->vHdr.dwUser = 0;
  205. lpcp->vHdr.dwFlags = 0;
  206. // Save the current format
  207. lpcp->lpbiSave = DibGetCurrentFormat (lpcs);
  208. // Make sure we can set the format to 16 bit RGB
  209. if(dwError = videoConfigure( lpcs->hVideoIn, DVM_FORMAT,
  210. VIDEO_CONFIGURE_SET, NULL,
  211. (LPBITMAPINFOHEADER)&lpcp->bi16, sizeof(BITMAPINFOHEADER),
  212. NULL, NULL ) ) {
  213. goto PalInitError;
  214. }
  215. // Put everything back the way it was
  216. if (dwError = videoConfigure( lpcs->hVideoIn, DVM_FORMAT,
  217. VIDEO_CONFIGURE_SET, NULL,
  218. (LPBITMAPINFOHEADER)lpcp->lpbiSave, lpcp->lpbiSave->bmiHeader.biSize,
  219. NULL, NULL )) {
  220. goto PalInitError;
  221. }
  222. PalInitError:
  223. return dwError;
  224. }
  225. //
  226. // Free resources used for palette capture
  227. //
  228. DWORD CapturePaletteFini (LPCAPSTREAM lpcs, LPCAPPAL lpcp)
  229. {
  230. if (lpcp->lpBits)
  231. GlobalFreePtr (lpcp->lpBits);
  232. if (lpcp->lp16to8)
  233. GlobalFreePtr (lpcp->lp16to8);
  234. if (lpcp->lpHistogram)
  235. FreeHistogram(lpcp->lpHistogram);
  236. if (lpcp->lpbiSave)
  237. GlobalFreePtr (lpcp->lpbiSave);
  238. return DV_ERR_OK;
  239. }
  240. //
  241. // CapturePaletteFrames() The workhorse of capture palette.
  242. //
  243. DWORD CapturePaletteFrames (LPCAPSTREAM lpcs, LPCAPPAL lpcp, int nCount)
  244. {
  245. int j;
  246. DWORD dwError;
  247. // switch to RGB16 format
  248. if (dwError = videoConfigure( lpcs->hVideoIn,
  249. DVM_FORMAT,
  250. VIDEO_CONFIGURE_SET, NULL,
  251. (LPBITMAPINFOHEADER)&lpcp->bi16, sizeof(BITMAPINFOHEADER),
  252. NULL, NULL ))
  253. goto CaptureFramesError;
  254. for (j = 0; j < nCount; j++){
  255. // Get a frame
  256. dwError = videoFrame(lpcs->hVideoIn, &lpcp->vHdr);
  257. // Let the user see it
  258. InvalidateRect (lpcs->hwnd, NULL, TRUE);
  259. UpdateWindow (lpcs->hwnd);
  260. // Histogram it
  261. DibHistogram(&lpcp->bi16, lpcp->lpBits, 0, 0, -1, -1, lpcp->lpHistogram);
  262. lpcp->wNumFrames++;
  263. }
  264. dwError = videoConfigure( lpcs->hVideoIn,
  265. DVM_FORMAT,
  266. VIDEO_CONFIGURE_SET, NULL,
  267. (LPBITMAPINFOHEADER)lpcp->lpbiSave,
  268. lpcp->lpbiSave->bmiHeader.biSize,
  269. NULL, NULL );
  270. // videoFrame( lpcs->hVideoIn, &lpcs->VidHdr );
  271. CaptureFramesError:
  272. return dwError;
  273. }
  274. //
  275. // CapturePaletteAuto() capture a palette from the video source
  276. // without user intervention.
  277. // Returns TRUE on success, FALSE on error
  278. //
  279. BOOL CapturePaletteAuto (LPCAPSTREAM lpcs, int nCount, int nColors)
  280. {
  281. HPALETTE hpal;
  282. HCURSOR hOldCursor;
  283. DWORD dwError = DV_ERR_OK;
  284. CAPPAL cappal;
  285. LPCAPPAL lpcp;
  286. lpcp = &cappal;
  287. if (nColors == 0)
  288. nColors = 256;
  289. lpcp->wNumColors = min (nColors, 256);
  290. if (nCount <= 0) // Bug 175
  291. nCount = 1;
  292. if (dwError = CapturePaletteInit (lpcs, lpcp))
  293. goto PalAutoExit;
  294. hOldCursor = SetCursor(lpcs->hWaitCursor);
  295. CapturePaletteFrames (lpcs, lpcp, nCount);
  296. /* we grabbed a frame, time to compute a palette */
  297. statusUpdateStatus(lpcs, IDS_CAP_STAT_OPTPAL_BUILD);
  298. // The HPALETTE returned in the following becomes
  299. // our "global" palette, hence is not deleted here.
  300. hpal = HistogramPalette(lpcp->lpHistogram, lpcp->lp16to8, lpcp->wNumColors);
  301. // Send driver both the pal and xlate table
  302. PalSendPaletteToDriver(lpcs, hpal, (LPBYTE)lpcp->lp16to8 );
  303. videoFrame( lpcs->hVideoIn, &lpcs->VidHdr ); // Update the display with a new image
  304. SetCursor(hOldCursor);
  305. InvalidateRect(lpcs->hwnd, NULL, TRUE);
  306. UpdateWindow(lpcs->hwnd);
  307. lpcs->fUsingDefaultPalette = FALSE;
  308. PalAutoExit:
  309. CapturePaletteFini (lpcs, lpcp);
  310. statusUpdateStatus(lpcs, NULL);
  311. // If an error happened, display it
  312. if (dwError)
  313. errorDriverID (lpcs, dwError);
  314. return (dwError == DV_ERR_OK);
  315. }
  316. //
  317. // CapturePaletteManual() capture a palette from the video source
  318. // with user intervention.
  319. // fGrab is TRUE on all but the last frame captured
  320. // Returns TRUE on success, FALSE on error
  321. //
  322. BOOL CapturePaletteManual (LPCAPSTREAM lpcs, BOOL fGrab, int nColors)
  323. {
  324. HPALETTE hpal;
  325. HCURSOR hOldCursor;
  326. LPCAPPAL lpcp;
  327. DWORD dwError = DV_ERR_OK;
  328. hOldCursor = SetCursor(lpcs->hWaitCursor);
  329. // We're initializing for the first time, so alloc everything
  330. if (lpcs->lpCapPal == NULL) {
  331. if (lpcp = (LPCAPPAL) GlobalAllocPtr (GHND, sizeof(CAPPAL))) {
  332. lpcs->lpCapPal = lpcp;
  333. if (nColors == 0)
  334. nColors = 256;
  335. lpcp->wNumColors = min (nColors, 256);
  336. dwError = CapturePaletteInit (lpcs, lpcp);
  337. }
  338. else
  339. dwError = IDS_CAP_OUTOFMEM;
  340. }
  341. lpcp = lpcs->lpCapPal;
  342. if (dwError != DV_ERR_OK)
  343. goto PalManualExit;
  344. // Add a frame to the histogram
  345. // Handle the case of telling us to stop before we ever started
  346. if (fGrab || !fGrab && (lpcp->wNumFrames == 0)) {
  347. CapturePaletteFrames (lpcs, lpcp, 1);
  348. lpcs->fUsingDefaultPalette = FALSE;
  349. }
  350. // All done, send the new palette to the driver
  351. if (!fGrab) {
  352. statusUpdateStatus(lpcs, IDS_CAP_STAT_OPTPAL_BUILD);
  353. // The HPALETTE returned in the following becomes
  354. // our "global" palette, hence is not deleted here.
  355. hpal = HistogramPalette(lpcp->lpHistogram,
  356. lpcp->lp16to8, lpcp->wNumColors);
  357. // Send driver both the pal and xlate table
  358. PalSendPaletteToDriver(lpcs, hpal, (LPBYTE)lpcp->lp16to8 );
  359. }
  360. videoFrame( lpcs->hVideoIn, &lpcs->VidHdr ); // Update the display with a new image
  361. InvalidateRect(lpcs->hwnd, NULL, TRUE);
  362. UpdateWindow(lpcs->hwnd);
  363. PalManualExit:
  364. if (!fGrab || (dwError != DV_ERR_OK)) {
  365. if (lpcp != NULL) {
  366. CapturePaletteFini (lpcs, lpcp);
  367. GlobalFreePtr (lpcp);
  368. lpcs->lpCapPal = NULL;
  369. }
  370. }
  371. SetCursor(hOldCursor);
  372. statusUpdateStatus(lpcs, NULL);
  373. // If an error happened, display it
  374. if (dwError) {
  375. errorUpdateError (lpcs, (WORD) dwError);
  376. lpcs-> dwReturn = dwError;
  377. }
  378. return (dwError == DV_ERR_OK);
  379. }
  380. /*--------------------------------------------------------------+
  381. | fileSavePalette - save the current palette in a file |
  382. | |
  383. +--------------------------------------------------------------*/
  384. BOOL FAR PASCAL fileSavePalette(LPCAPSTREAM lpcs, LPSTR lpszFileName)
  385. {
  386. HPALETTE hpal;
  387. HMMIO hmmio;
  388. WORD w;
  389. HCURSOR hOldCursor;
  390. MMCKINFO ckRiff;
  391. MMCKINFO ck;
  392. int nColors;
  393. FCLOGPALETTE pal;
  394. BOOL fOK = FALSE;
  395. if ((hpal = lpcs->hPalCurrent) == NULL)
  396. return FALSE;
  397. hmmio = mmioOpen(lpszFileName, NULL, MMIO_WRITE);
  398. if( !hmmio ) {
  399. /* try and create */
  400. hmmio = mmioOpen(lpszFileName, NULL, MMIO_CREATE | MMIO_WRITE);
  401. if( !hmmio ) {
  402. /* find out if the file was read only or we are just */
  403. /* totally hosed up here. */
  404. hmmio = mmioOpen(lpszFileName, NULL, MMIO_READ);
  405. if (hmmio){
  406. /* file was read only, error on it */
  407. errorUpdateError (lpcs, IDS_CAP_READONLYFILE, (LPSTR)lpszFileName);
  408. mmioClose(hmmio, 0);
  409. return FALSE;
  410. } else {
  411. /* even weirder error has occured here, give CANTOPEN */
  412. errorUpdateError (lpcs, IDS_CAP_CANTOPEN, (LPSTR) lpszFileName);
  413. return FALSE;
  414. }
  415. }
  416. }
  417. hOldCursor = SetCursor( lpcs-> hWaitCursor );
  418. /* Seek to beginning of file, so we can write the header. */
  419. mmioSeek(hmmio, 0, SEEK_SET);
  420. /* Create RIFF chunk */
  421. ckRiff.fccType = mmioFOURCC('P','A','L',' ');
  422. if(mmioCreateChunk (hmmio,&ckRiff,MMIO_CREATERIFF)) {
  423. goto FileError;
  424. }
  425. /* Create Palette chunk */
  426. ck.cksize = 0;
  427. ck.ckid = mmioFOURCC('d','a','t','a');
  428. if(mmioCreateChunk(hmmio,&ck,0)) {
  429. goto FileError;
  430. }
  431. // Get the palette data here
  432. GetObject(hpal, sizeof(int), (LPVOID)&nColors);
  433. pal.palVersion = 0x0300;
  434. pal.palNumEntries = nColors;
  435. GetPaletteEntries(hpal, 0, nColors, pal.palPalEntry);
  436. // Calc the size of the logpalette
  437. // which is the sizeof palVersion + sizeof palNumEntries + colors
  438. w = sizeof (WORD) + sizeof (WORD) + nColors * sizeof (PALETTEENTRY);
  439. // Write out the palette
  440. if(mmioWrite(hmmio, (LPSTR)&pal, (DWORD) w) != (LONG) w) {
  441. goto FileError;
  442. }
  443. if(mmioAscend(hmmio, &ck, 0)) {
  444. goto FileError;
  445. }
  446. if(mmioAscend(hmmio, &ckRiff, 0)) {
  447. goto FileError;
  448. }
  449. fOK = TRUE;
  450. FileError:
  451. mmioClose( hmmio, 0 );
  452. SetCursor( hOldCursor );
  453. if (!fOK)
  454. errorUpdateError (lpcs, IDS_CAP_ERRORPALSAVE, (LPSTR) lpszFileName);
  455. return fOK;
  456. }
  457. /*--------------------------------------------------------------+
  458. | fileOpenPalette - use a new palette from the specified file |
  459. | |
  460. +--------------------------------------------------------------*/
  461. BOOL FAR PASCAL fileOpenPalette(LPCAPSTREAM lpcs, LPSTR lpszFileName)
  462. {
  463. HPALETTE hpal;
  464. HMMIO hmmio;
  465. WORD w;
  466. HCURSOR hOldCursor;
  467. MMCKINFO ckRiff;
  468. MMCKINFO ck;
  469. FCLOGPALETTE pal;
  470. BOOL fOK = FALSE;
  471. if ((hpal = lpcs->hPalCurrent) == NULL)
  472. return FALSE;
  473. hmmio = mmioOpen(lpszFileName, NULL, MMIO_READ);
  474. if( !hmmio ) {
  475. errorUpdateError (lpcs, IDS_CAP_ERRORPALOPEN, (LPSTR) lpszFileName);
  476. return FALSE;
  477. }
  478. hOldCursor = SetCursor( lpcs-> hWaitCursor );
  479. /* Seek to beginning of file, so we can read the header. */
  480. mmioSeek(hmmio, 0, SEEK_SET);
  481. /* Find the RIFF chunk */
  482. ckRiff.fccType = mmioFOURCC('P','A','L',' ');
  483. if(mmioDescend (hmmio, &ckRiff, NULL, MMIO_FINDRIFF)) {
  484. goto PalOpenError;
  485. }
  486. /* Find the data chunk */
  487. ck.cksize = 0;
  488. ck.ckid = mmioFOURCC('d','a','t','a');
  489. if(mmioDescend (hmmio, &ck, &ckRiff, MMIO_FINDCHUNK)) {
  490. goto PalOpenError;
  491. }
  492. // First read just the Version and number of entries
  493. // which is the sizeof palVersion + sizeof palNumEntries
  494. w = sizeof (WORD) + sizeof (WORD);
  495. if(mmioRead(hmmio, (LPSTR)&pal, (DWORD) w) != (LONG) w) {
  496. goto PalOpenError;
  497. }
  498. // Do a bit of checking
  499. if ((pal.palVersion != 0x0300) || (pal.palNumEntries > 256))
  500. goto PalOpenError;
  501. // Now get the actual palette data
  502. // which is the sizeof palVersion + sizeof palNumEntries
  503. w = pal.palNumEntries * sizeof (PALETTEENTRY);
  504. if(mmioRead(hmmio, (LPSTR)&pal.palPalEntry, (DWORD) w) != (LONG) w) {
  505. goto PalOpenError;
  506. }
  507. if (hpal = CreatePalette ((LPLOGPALETTE) &pal)) {
  508. PalSendPaletteToDriver (lpcs, hpal, NULL /*lpXlateTable */);
  509. fOK = TRUE;
  510. }
  511. videoFrame( lpcs->hVideoIn, &lpcs->VidHdr ); // grab a new frame
  512. PalOpenError:
  513. mmioClose( hmmio, 0 );
  514. SetCursor( hOldCursor );
  515. InvalidateRect(lpcs->hwnd, NULL, TRUE);
  516. UpdateWindow(lpcs->hwnd); // update the display with new frame
  517. if (!fOK)
  518. errorUpdateError (lpcs, IDS_CAP_ERRORPALOPEN, (LPSTR) lpszFileName);
  519. else
  520. lpcs->fUsingDefaultPalette = FALSE;
  521. return fOK;
  522. }