|
|
/****************************************************************************
* * 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. * ***************************************************************************/
#define INC_OLE2
#pragma warning(disable:4103)
#include <windows.h>
#include <windowsx.h>
#include <win32.h>
#include <mmsystem.h>
#include <vfw.h>
#include "ivideo32.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
CopyMemory (lpcs->lpBitsInfo, 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;
// Allow room for a RIFF chunk prepended to the actual image,
// and a junk chunk on the tail end
#define RESERVE_FOR_RIFF (512+sizeof(RIFF))
dwSize = lpbih->biSizeImage + RESERVE_FOR_RIFF;
if (lpcs->lpBitsUnaligned) { #ifdef CHICAGO
vidxFreePreviewBuffer (lpcs->hVideoIn, lpcs->lpBitsUnaligned); #else
FreeSectorAlignedMem (lpcs->lpBitsUnaligned); #endif
lpcs->lpBitsUnaligned = NULL; lpcs->lpBits = NULL; }
#ifdef CHICAGO
if (MMSYSERR_NOERROR == vidxAllocPreviewBuffer ( lpcs->hVideoIn, &lpcs->lpBitsUnaligned, dwSize)) { lpcs->lpBits = (LPBYTE) (ROUNDUPTOSECTORSIZE(lpcs->lpBitsUnaligned, 512) + sizeof(RIFF)); }
#else
if (lpcs->lpBitsUnaligned = ((LPBYTE)AllocSectorAlignedMem (dwSize, 512))) { lpcs->lpBits = (LPBYTE) (ROUNDUPTOSECTORSIZE(lpcs->lpBitsUnaligned, 512) + sizeof(RIFF)); } #endif
if (!lpcs->lpBits) return (DV_ERR_NOMEM);
return DV_ERR_OK; }
//
// Dib Initialization 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) { #ifdef CHICAGO
vidxFreePreviewBuffer (lpcs->hVideoIn, lpcs->lpBitsUnaligned); #else
FreeSectorAlignedMem (lpcs->lpBitsUnaligned); #endif
lpcs->lpBits = NULL; lpcs->lpBitsUnaligned = 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, 0 ) ) { return dwError; } else { videoSetRect (lpcs->hVideoCapture, DVM_DST_RECT, rc); videoSetRect (lpcs->hVideoIn, DVM_SRC_RECT, rc); videoSetRect (lpcs->hVideoIn, DVM_DST_RECT, rc); } 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, 0, 0, NULL, 0);
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, 0 ) ) { // 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, UINT 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) { errorUpdateError(lpcs, IDS_CAP_AVI_DRAWDIB_ERROR); //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; short 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 + (UINT)lpbi->biSize); lpBits = lpcs->lpBits;
GetObject(hPalNew, sizeof(short), (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, #if defined _WIN32 && defined UNICODE
0 // we don't support BACKGROUNDPAL yet in drawdib
#else
DDF_BACKGROUNDPAL #endif
); } else fOK = DrawDibDraw(lpcs->hdd, hdc, 0, 0, lpcs->dxBits, lpcs->dyBits, (LPBITMAPINFOHEADER)lpcs->lpBitsInfo, lpcs->lpBits, 0, 0, -1, -1, #if defined _WIN32 && defined UNICODE
0 // we don't support BACKGROUNDPAL yet in drawdib
#else
DDF_BACKGROUNDPAL #endif
);
} 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) {
LPBITMAPINFO lpOutFormat = NULL; HANDLE hPackedDIBOut = NULL;
if (!(lpOutFormat = (LPBITMAPINFO)GlobalAllocPtr( GMEM_MOVEABLE, sizeof (BITMAPINFOHEADER) + 256 * sizeof (RGBQUAD)))) return NULL;
CopyMemory (lpOutFormat, 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
//
CopyMemory (lpbi, 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);
CopyMemory (lpBits, 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, LPTSTR 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; }
|