/****************************************************************************
 *
 *   capdib.c
 * 
 *   DIB processing module.
 *
 *   Microsoft Video for Windows Sample Capture Class
 *
 *   Copyright (c) 1992, 1993 Microsoft Corporation.  All Rights Reserved.
 *
 *    You have a royalty-free right to use, modify, reproduce and 
 *    distribute the Sample Files (and/or any modified version) in 
 *    any way you find useful, provided that you agree that 
 *    Microsoft has no warranty obligations or liability for any 
 *    Sample Application Files which are modified. 
 *
 ***************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <msvideo.h>
#include <drawdib.h>
#include "avicap.h"
#include "avicapi.h"


//
// Initialize a DIB to the default format of 160x120x8, BI_RGB
//
void SetDefaultCaptureFormat (LPBITMAPINFOHEADER lpbih)
{
    lpbih->biSize              = sizeof (BITMAPINFOHEADER);
    lpbih->biWidth             = 160;
    lpbih->biHeight            = 120;
    lpbih->biBitCount          = 8;
    lpbih->biPlanes            = 1;
    lpbih->biCompression       = BI_RGB;
    lpbih->biSizeImage         = DIBWIDTHBYTES (*lpbih) * lpbih->biHeight;
    lpbih->biXPelsPerMeter     = 0;
    lpbih->biYPelsPerMeter     = 0;
    lpbih->biClrUsed           = 256;
    lpbih->biClrImportant      = 0;
}

// 
// Whenever we get a new format from the driver, OR
// start using a new palette, we must reallocate
// our global BITMAPINFOHEADER.  This allows JPEG
// quantization tables to be tacked onto the BITMAPINFO
// or any other format specific stuff.  The color table
// is always offset biSize from the start of the BITMAPINFO.
// Returns: 0 on success, or DV_ERR_... code
// 

DWORD AllocNewGlobalBitmapInfo (LPCAPSTREAM lpcs, LPBITMAPINFOHEADER lpbi)
{
    DWORD dwSize;

    dwSize = lpbi->biSize + 256 * sizeof (RGBQUAD);

    // The 256 entry above is HARDWIRED ON PURPOSE
    // If biClrUsed was used instead, we would have to realloc
    // whenever a palette is pasted (during DibNewPalette())!!!

    if (lpcs->lpBitsInfo) 
         lpcs->lpBitsInfo = (LPBITMAPINFO) GlobalReAllocPtr (lpcs->lpBitsInfo,
                dwSize, GHND);
    else
         lpcs->lpBitsInfo = (LPBITMAPINFO) GlobalAllocPtr (GHND, dwSize);

    if (!lpcs->lpBitsInfo)
         return (DV_ERR_NOMEM);

    // Copy over the BITMAPINFOHEADER
    hmemcpy ((HPSTR)lpcs->lpBitsInfo, (HPSTR)lpbi, lpbi->biSize);

    return DV_ERR_OK;
}

// 
// Whenever we get a new format from the driver
// allocate a new global bitspace.  This bitspace is used
// in preview mode and single frame capture.
// Returns: 0 on success, or DV_ERR_... code
// 

DWORD AllocNewBitSpace (LPCAPSTREAM lpcs, LPBITMAPINFOHEADER lpbih)
{
    DWORD dwSize;

    dwSize = lpbih->biSizeImage;

    if (lpcs->lpBits) 
         lpcs->lpBits = GlobalReAllocPtr (lpcs->lpBits, dwSize, GHND);
    else
         lpcs->lpBits = GlobalAllocPtr (GHND, dwSize);

    if (!lpcs->lpBits)
         return (DV_ERR_NOMEM);

    return DV_ERR_OK;    
}

//
// Dib Inititialization code
// Returns: 0 on success, or DV_ERR_... code
//

DWORD DibInit (LPCAPSTREAM lpcs)
{
    BITMAPINFOHEADER bmih;
    
    SetDefaultCaptureFormat (&bmih);
    return ((WORD) AllocNewGlobalBitmapInfo (lpcs, &bmih));
}

//
// Fini code to free all bitmap resources
//
void DibFini (LPCAPSTREAM lpcs)
{
    if (lpcs->lpBits) {
        GlobalFreePtr (lpcs->lpBits);
        lpcs->lpBits = NULL;
    }
    if (lpcs->lpBitsInfo) {
        GlobalFreePtr (lpcs->lpBitsInfo);
        lpcs->lpBitsInfo = NULL;
    }
    lpcs->dxBits = 0;
    lpcs->dyBits = 0;
}

//
// Send a format to the driver.
// Whenever we do a format change, send the driver the 
// Source and destination rects.
// Returns: 0 on success, or DV_ERR_... code
//
DWORD SendDriverFormat (LPCAPSTREAM lpcs, LPBITMAPINFOHEADER lpbih, DWORD dwInfoHeaderSize)
{
    RECT rc;
    DWORD dwError = DV_ERR_NOTSUPPORTED;

    rc.left = rc.top = 0;
    rc.right = (int) lpbih->biWidth;
    rc.bottom = (int) lpbih->biHeight;

    if (dwError = videoConfigure(lpcs->hVideoIn,
            DVM_FORMAT,
            VIDEO_CONFIGURE_SET, NULL, 
            (LPBITMAPINFOHEADER)lpbih, dwInfoHeaderSize,
            NULL, NULL ) ) {
        return dwError;
    }
    else {
         // Set the ExternalIn Destination rectangle to the same size
         videoMessage (lpcs->hVideoCapture,
                DVM_DST_RECT, 
                (DWORD) (LPVOID)&rc, VIDEO_CONFIGURE_SET);

         // Set the VideoIn Source Rectangle to the same size
         videoMessage (lpcs->hVideoIn,
                DVM_SRC_RECT, 
                (DWORD) (LPVOID)&rc, VIDEO_CONFIGURE_SET);

         // Set the VideoIn Destination Rectangle to the same size
         videoMessage (lpcs->hVideoIn,
                DVM_DST_RECT, 
                (DWORD) (LPVOID)&rc, VIDEO_CONFIGURE_SET);
    }
    return dwError;
}


//
// Given a DIB, see if the driver likes it, then
//  allocate the global BITMAPINFOHEADER and bitspace.  
//
//
DWORD SetFormatFromDIB (LPCAPSTREAM lpcs, LPBITMAPINFOHEADER lpbih)
{
    DWORD dwError;

    // Fill optional fields in the DIB header
    if (lpbih->biSizeImage == 0) 
        lpbih->biSizeImage = DIBWIDTHBYTES (*lpbih) * lpbih->biHeight;

    // Is the format palatized or full-color
    if (lpbih->biBitCount <= 8 && lpbih->biClrUsed == 0)
        lpbih->biClrUsed = (1 << lpbih-> biBitCount);     // paletized

    // See if the driver will support it
    if (dwError = SendDriverFormat (lpcs, lpbih, lpbih->biSize) )
        return dwError;

    // Realloc our global header
    if (dwError = AllocNewGlobalBitmapInfo (lpcs, lpbih))
        return dwError;

    // Realloc the bits
    if (dwError = AllocNewBitSpace (lpcs, lpbih))
        return dwError;

    lpcs->dxBits = (int)lpbih->biWidth;
    lpcs->dyBits = (int)lpbih->biHeight;

    lpcs->VidHdr.lpData = lpcs->lpBits;
    lpcs->VidHdr.dwBufferLength = lpbih->biSizeImage;
    lpcs->VidHdr.dwUser = 0;
    lpcs->VidHdr.dwFlags = 0;
    
    return (DV_ERR_OK);
}


//
// Returns: a LPBITMAPINFO allocated from global memory
//      containing the current format, or NULL on error.
//      Note that this structure can be larger than 
//      sizeof (BITMAPINFO), ie. JPEG !!!
//

LPBITMAPINFO DibGetCurrentFormat (LPCAPSTREAM lpcs)
{
    DWORD               dwError;
    DWORD               dwSize = 0;
    LPBITMAPINFO        lpBInfo = NULL;

    if (!lpcs->fHardwareConnected)
        return NULL;

    // How large is the BITMAPINFOHEADER?
    videoConfigure( lpcs->hVideoIn,
            DVM_FORMAT,
             VIDEO_CONFIGURE_GET | VIDEO_CONFIGURE_QUERYSIZE,
             &dwSize, NULL, NULL, NULL, NULL);

    if (!dwSize)
        dwSize = sizeof (BITMAPINFOHEADER);

    if (!(lpBInfo = (LPBITMAPINFO) GlobalAllocPtr (GMEM_MOVEABLE, dwSize)))
         return (NULL);

    if (dwError = videoConfigure( lpcs->hVideoIn,
            DVM_FORMAT,
             VIDEO_CONFIGURE_GET | VIDEO_CONFIGURE_CURRENT, NULL, 
             (LPBITMAPINFOHEADER) lpBInfo, dwSize,
             NULL, NULL ) ) {
        // very bad. the driver can't tell us its format. we're hosed.
        GlobalFreePtr (lpBInfo);
        return NULL;
     }

    return (lpBInfo);
}

//
// Main entry point when changing capture formats.
// This is called when the user closes the drivers format dialog.
// Returns: 0 on success, or DV_ERR_... code
//
DWORD DibGetNewFormatFromDriver (LPCAPSTREAM lpcs)
{
    BOOL                f;
    BITMAPINFOHEADER    bih;
    DWORD               dwError;
    LPBITMAPINFO        lpBInfo;

    if (!lpcs->fHardwareConnected)
        return DV_ERR_OK;       // Return OK if no hardware exists

    lpBInfo = DibGetCurrentFormat (lpcs);

    if (lpBInfo == NULL)
        return DV_ERR_NOTSUPPORTED;

    // Set our internal state
    if (dwError = SetFormatFromDIB (lpcs, (LPBITMAPINFOHEADER) lpBInfo)) {
        // couldn't change formats, time to punt!
        // Try to switch back to minimal format (120x160x8)

        errorDriverID (lpcs, dwError);

        SetDefaultCaptureFormat (&bih);
        dwError = SetFormatFromDIB (lpcs, &bih);
    }

    // Force a new frame to be taken, so the DIB contains good
    // data.  Especially important to prevent codecs from exploding!
    if (!dwError)
        videoFrame (lpcs->hVideoIn, &lpcs->VidHdr);

    if (lpBInfo) 
        GlobalFreePtr (lpBInfo);

    f = DrawDibBegin(lpcs->hdd,NULL,-1,-1,(LPBITMAPINFOHEADER)(lpcs->lpBitsInfo),-1,-1,0);
    if (!f)
        errorUpdateError (lpcs, IDS_CAP_AVI_DRAWDIB_ERROR);

    return (dwError);
}

//
// Main entry point when changing capture formats via App message.
// Returns: TRUE on success, or FALSE if format not supported
//
BOOL DibNewFormatFromApp (LPCAPSTREAM lpcs, LPBITMAPINFO lpbiNew, WORD dwSize)
{
    BOOL                f;
    DWORD               dwError;
    LPBITMAPINFO        lpBInfo;

    if (!lpcs->fHardwareConnected)
        return FALSE;

    lpBInfo = DibGetCurrentFormat (lpcs);  // Allocs memory!!!

    if (lpBInfo == NULL)
        return FALSE;

    // Set our internal state
    if (dwError = SetFormatFromDIB (lpcs, (LPBITMAPINFOHEADER) lpbiNew)) {
        // Driver didn't accept the format, 
        // switch back to the original

        errorDriverID (lpcs, dwError);

        SetFormatFromDIB (lpcs, (LPBITMAPINFOHEADER)lpBInfo);
    }

    // Force a new frame to be taken, so the DIB contains good
    // data.  Especially important to prevent codecs from exploding!
    videoFrame (lpcs->hVideoIn, &lpcs->VidHdr);

    if (lpBInfo) 
        GlobalFreePtr (lpBInfo);

    f = DrawDibBegin(lpcs->hdd,NULL,-1,-1,(LPBITMAPINFOHEADER)(lpcs->lpBitsInfo),-1,-1,0);
    if (!f)
        errorDriverID (lpcs, IDS_CAP_AVI_DRAWDIB_ERROR);

    return (dwError == DV_ERR_OK);
}


void xlatClut8 (BYTE _huge *pb, DWORD dwSize, BYTE _huge *xlat)
{
    DWORD dw;

    for (dw = 0; dw < dwSize; dw++, ((BYTE huge *)pb)++)
        *pb = xlat[*pb];
}

//
// DibNewPalette
//
// Performs three functions:
// 1. Updates the biClrUsed field if biBitCount <= 8.
// 2. Remaps BI_RGB images through a LUT when a new palette is assigned.
// 3. Copies the palette entries into our global BITMAPINFO
//
// Returns: TRUE on success
//
DWORD DibNewPalette (LPCAPSTREAM lpcs, HPALETTE hPalNew)
{
    LPBITMAPINFOHEADER  lpbi;
    int                 n;
    int                 nColors;
    BYTE FAR *          lpBits;
    RGBQUAD FAR *       lpRgb;
    BYTE                xlat[256];
    DWORD               dwSize;
    PALETTEENTRY        pe;

    if (!hPalNew || !lpcs->lpBits || !lpcs->lpBitsInfo)
        return FALSE;

    lpbi   = &(lpcs->lpBitsInfo->bmiHeader);
    lpRgb  = (RGBQUAD FAR *)((LPSTR)lpbi + (WORD)lpbi->biSize);
    lpBits = lpcs->lpBits;

    GetObject(hPalNew, sizeof(int), (LPSTR) &nColors);
    if (nColors > 256)
        nColors = 256;

    // Get the palette entries regardless of the compression
    // Supermac uses non BI_RGB with a palette!

    if (lpbi->biBitCount == 8) {
        for (n=0; n<nColors; n++) {
            GetPaletteEntries(hPalNew, n, 1, &pe);
            lpRgb[n].rgbRed   = pe.peRed;
            lpRgb[n].rgbGreen = pe.peGreen;
            lpRgb[n].rgbBlue  = pe.peBlue;
        }
    }

    if (lpbi->biBitCount == 8 && lpbi->biCompression == BI_RGB) {

        //
        //  build a xlat table. from the old Palette to the new palette.
        //
        for (n=0; n<(int)lpbi->biClrUsed; n++) {
            xlat[n] = (BYTE)GetNearestPaletteIndex(hPalNew,
                RGB(lpRgb[n].rgbRed,lpRgb[n].rgbGreen,lpRgb[n].rgbBlue));
        }

        //
        // translate the DIB bits
        //
        if ((dwSize = lpbi->biSizeImage) == 0)
            dwSize = lpbi->biHeight * DIBWIDTHBYTES(*lpbi);

        switch ((WORD)lpbi->biCompression)
        {
            case BI_RGB:
                xlatClut8(lpBits, dwSize, xlat);
        }
    }

    // Fix for Supermac, force biClrUsed to the number of palette entries
    // even if non-BI_RGB formats.

    if (lpbi-> biBitCount <= 8)
        lpbi->biClrUsed = nColors;

    return TRUE;
}


/* DibPaint(LPCAPSTREAM lpcs, hdc)
 *
 * Paint the current DIB into the window;
 */
void DibPaint(LPCAPSTREAM lpcs, HDC hdc)
{
    RECT        rc;
    BOOL        fOK;
    
    fOK = (lpcs->lpBits != NULL);
    
    if (fOK) {
        if (lpcs-> fScale) {
            GetClientRect(lpcs->hwnd, &rc);
            fOK = DrawDibDraw(lpcs->hdd, hdc, 0, 0, 
                  rc.right - rc.left, rc.bottom - rc.top,
                  (LPBITMAPINFOHEADER)lpcs->lpBitsInfo, lpcs->lpBits,
                   0, 0, -1, -1, DDF_BACKGROUNDPAL);
        }
        else 
            fOK = DrawDibDraw(lpcs->hdd, hdc, 0, 0, 
                lpcs->dxBits, lpcs->dyBits,
                (LPBITMAPINFOHEADER)lpcs->lpBitsInfo, lpcs->lpBits,
                0, 0, -1, -1, DDF_BACKGROUNDPAL);
    }
    if (!fOK) {
        SelectObject(hdc, GetStockObject(BLACK_BRUSH));
        GetClientRect(lpcs->hwnd, &rc);
        PatBlt(hdc, 0, 0, rc.right, rc.bottom, PATCOPY);
    }
}

/*
 *
 * CreatePackedDib() - return the current DIB in packed (ie CF_DIB) format
 *
 */

HANDLE CreatePackedDib (LPBITMAPINFO lpBitsInfo, LPSTR lpSrcBits, HPALETTE hPalette)
{
    HANDLE              hdib;
    LPBITMAPINFO        lpbi;
    int                 i;
    DWORD               dwSize;
    PALETTEENTRY        pe;
    LPBYTE              lpBits;
    RGBQUAD FAR *       lpRgb;

   // If the data is compressed, let ICM do the work for us...
    if ( lpBitsInfo->bmiHeader.biCompression != BI_RGB &&
         lpBitsInfo->bmiHeader.biCompression != BI_RLE8 &&
        (lpBitsInfo->bmiHeader.biBitCount != 8 ||
         lpBitsInfo->bmiHeader.biBitCount != 24 )) {

        LPBITMAPINFO lpOutFormat = NULL;
        HANDLE hPackedDIBOut = NULL;

        if (!(lpOutFormat = (LPBITMAPINFO)GlobalAllocPtr(
                        GMEM_MOVEABLE, sizeof (BITMAPINFOHEADER) + 
                        256 * sizeof (RGBQUAD))))
            return NULL;

        hmemcpy ((HPSTR)lpOutFormat, (HPSTR)lpBitsInfo, sizeof (BITMAPINFOHEADER));

        // Try to get an RGB format
        lpOutFormat->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
        lpOutFormat->bmiHeader.biCompression = BI_RGB;
        lpOutFormat->bmiHeader.biClrUsed = 0;
        lpOutFormat->bmiHeader.biClrImportant = 0;

        // Uh, oh, force to a 24-bit DIB if > 8 BPP
        if (lpBitsInfo->bmiHeader.biBitCount <= 8) 
            lpOutFormat->bmiHeader.biBitCount = 8;
        else
            lpOutFormat->bmiHeader.biBitCount = 24;

        lpOutFormat->bmiHeader.biSizeImage = 
                WIDTHBYTES (lpOutFormat->bmiHeader.biWidth * 
                (lpOutFormat->bmiHeader.biBitCount == 8 ? 1 : 3)) * 
                lpOutFormat->bmiHeader.biHeight;

        hPackedDIBOut = ICImageDecompress (
                NULL,           /*hic*/ 
                0,              /*uiFlags*/
                lpBitsInfo,     /*lpbiIn*/
                lpSrcBits,      /*lpBits*/
                lpOutFormat);   /*use default format chosen by compressor*/

        if (lpOutFormat)
            GlobalFreePtr (lpOutFormat);

        return (hPackedDIBOut);
    }

    dwSize = lpBitsInfo->bmiHeader.biSize +
              lpBitsInfo->bmiHeader.biClrUsed * sizeof(RGBQUAD) +
              lpBitsInfo->bmiHeader.biSizeImage;

    hdib = GlobalAlloc(GMEM_MOVEABLE, dwSize);

    if (!hdib)
         return NULL;

    lpbi = (LPVOID)GlobalLock(hdib);

    //
    // copy the header
    //
    hmemcpy ((HPSTR)lpbi, (HPSTR)lpBitsInfo, lpBitsInfo->bmiHeader.biSize);
    
    //
    // copy the color table
    //
    lpRgb  = (RGBQUAD FAR *)((LPSTR)lpbi + (WORD)lpbi->bmiHeader.biSize);
    for (i=0; i < (int)lpBitsInfo->bmiHeader.biClrUsed; i++) {
        GetPaletteEntries(hPalette, i, 1, &pe);
        lpRgb[i].rgbRed   = pe.peRed;
        lpRgb[i].rgbGreen = pe.peGreen;
        lpRgb[i].rgbBlue  = pe.peBlue;
        lpRgb[i].rgbReserved = 0;
    }

    //
    // copy the bits.
    //
    lpBits  =   (LPBYTE)lpbi + 
                lpbi->bmiHeader.biSize +
                lpbi->bmiHeader.biClrUsed * sizeof(RGBQUAD);

    hmemcpy ((LPSTR)lpBits, (LPSTR)lpSrcBits, 
                lpbi->bmiHeader.biSizeImage);

    GlobalUnlock (hdib);

    return hdib;
 }


 /*---------------------------------------------------------------------+
 | dibIsWritable() - return TRUE if the dib format is writable,                  |
 |                     by out dibWrite() function, FALSE if not.                 |
 |                                                                               |
 +---------------------------------------------------------------------*/
BOOL FAR PASCAL dibIsWritable (LPBITMAPINFO lpBitsInfo)
{
    if (!lpBitsInfo)
        return FALSE;

     // For now, just assume that all capture formats have an installed
     // codec which can convert to RGB.  In the future, each time the 
     // format is changed, test that the codec actually accepts the format.

     return TRUE;
 }
 
 
 /*---------------------------------------------------------------------+
 | dibWrite() - write out the DIB to a file. The global header is       |
 |                in <glpBitsInfo> and the actual dib bits are in                |
 |                <glpBits>.  If it is palettized then the palette is in         |
 |                <ghPalCurrent>.                                                |
 |                                                                               |
 |  We won't do error reporting in this function, let the caller take   |
 |  care of that along with Opening and Closing the HMMIO.              |
 |                                                                               |
 +---------------------------------------------------------------------*/
BOOL FAR PASCAL dibWrite(LPCAPSTREAM lpcs, HMMIO hmmio)
 {
     BITMAPFILEHEADER   bfh;
     DWORD              dw;
     HANDLE             hPackedDib = NULL;
     LPBITMAPINFO       lpbi = NULL;
     BOOL               fOK = FALSE;

     /* do some checking */
    WinAssert(hmmio != 0);
     
    if (!lpcs->lpBits || !lpcs->lpBitsInfo)
        return FALSE;

    // Create a packed DIB, converting from a compressed format,
    // if necessary.
    hPackedDib = CreatePackedDib (lpcs->lpBitsInfo,
                        lpcs->lpBits,
                        lpcs->hPalCurrent);

    lpbi = (LPBITMAPINFO) GlobalLock (hPackedDib);

    if (!lpbi)
        goto WriteError;

    /* initialize the bitmap file header */
    bfh.bfType = 'B' | 'M' << 8;
    bfh.bfSize = sizeof(bfh) + sizeof(BITMAPINFOHEADER) +
        lpbi->bmiHeader.biSizeImage + 
        (lpbi->bmiHeader.biBitCount > 8 ? 0 : (lpbi->bmiHeader.biClrUsed * sizeof(RGBQUAD)));
  
    bfh.bfReserved1 = bfh.bfReserved2 = 0;
    bfh.bfOffBits = bfh.bfSize - lpbi->bmiHeader.biSizeImage ;

    // dw is the size of the BITMAPINFO + color table + image
    dw = bfh.bfSize - sizeof(bfh);

    /* write out the file header portion */
    if (mmioWrite(hmmio, (HPSTR)&bfh, (LONG)sizeof(BITMAPFILEHEADER)) != 
                sizeof(BITMAPFILEHEADER)){
         goto WriteError;
    }
    
    /* now write out the header and bits */
    if (mmioWrite(hmmio, (HPSTR)lpbi, (LONG) dw) == (LONG) dw) {
         fOK = TRUE;
    }
    
WriteError:
    if (lpbi)
        GlobalUnlock (hPackedDib);
    if (hPackedDib)
        GlobalFree (hPackedDib);

    return fOK;
}

/*--------------------------------------------------------------+
| fileSaveDIB - save the frame as a DIB                         |
|   Top level routine to save a single frame                    |
+--------------------------------------------------------------*/
BOOL FAR PASCAL fileSaveDIB(LPCAPSTREAM lpcs, LPSTR lpszFileName)
{
    HMMIO               hmmio;
    HCURSOR             hOldCursor;
    BOOL                fOK;

    hmmio = mmioOpen(lpszFileName, NULL, MMIO_WRITE);
    if( !hmmio ) {
	/* try and create */    
        hmmio = mmioOpen(lpszFileName, NULL, MMIO_CREATE | MMIO_WRITE);
	if( !hmmio ) {
	    /* find out if the file was read only or we are just */
	    /* totally hosed up here.				 */
	    hmmio = mmioOpen(lpszFileName, NULL, MMIO_READ);
	    if (hmmio){
		/* file was read only, error on it */
                errorUpdateError (lpcs, IDS_CAP_READONLYFILE, (LPSTR)lpszFileName);
		mmioClose(hmmio, 0);
		return FALSE;
	    } else {
		/* even weirder error has occured here, give CANTOPEN */
                errorUpdateError (lpcs, IDS_CAP_CANTOPEN, (LPSTR) lpszFileName);
		return FALSE;
	    }
	}
    }

    hOldCursor = SetCursor( lpcs-> hWaitCursor );

    mmioSeek(hmmio, 0, SEEK_SET);

    fOK = dibWrite(lpcs, hmmio);

    mmioClose( hmmio, 0 );

    SetCursor( hOldCursor );

    if (!fOK)
       errorUpdateError (lpcs, IDS_CAP_ERRORDIBSAVE, (LPSTR) lpszFileName);

    return fOK;
}
    
