|
|
///////////////////////////////////////////////////////////////////////////////
//
// ICM.C
//
// Helper routines for compressing/decompressing/and choosing compressors.
//
// (C) Copyright Microsoft Corp. 1991-1995. 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.
//
// If you did not get this from Microsoft Sources, then it may not be the
// most current version. This sample code in particular will be updated
// and include more documentation.
//
// Sources are:
// CompuServe: WINSDK forum, MDK section.
// Anonymous FTP from ftp.uu.net vendor\microsoft\multimedia
//
///////////////////////////////////////////////////////////////////////////////
#pragma warning(disable:4103)
//
// define these before compman.h, so our functions get declared right.
//
#ifndef _WIN32
#define VFWAPI FAR PASCAL _loadds
#define VFWAPIV FAR CDECL _loadds
#endif
#include <win32.h>
#include <mmsystem.h>
#include <vfw.h>
#include <profile.h>
#ifdef DEBUG
static BOOL fDebug = -1; static void CDECL dprintf(LPSTR, ...); #define DPF(x) dprintf x
#else
#define DPF(x)
#endif
// macro to get the number of chars (byte or word) in a buffer
//
#if !defined NUMELMS
#define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
#endif
#define NOMMREG
#define NOMSACM
#define NOAVICAP
#include "icm.rc"
#define AVIStreamGetFrameOpen XAVIStreamGetFrameOpen
#define AVIStreamGetFrame XAVIStreamGetFrame
#define AVIStreamGetFrameClose XAVIStreamGetFrameClose
HMODULE havifile; PGETFRAME (STDAPICALLTYPE *XAVIStreamGetFrameOpen)(PAVISTREAM pavi, LPBITMAPINFOHEADER lpbiWanted); LPVOID (STDAPICALLTYPE *XAVIStreamGetFrame)(PGETFRAME pgf, LONG pos); HRESULT (STDAPICALLTYPE *XAVIStreamGetFrameClose)(PGETFRAME pgf);
#ifdef _WIN32
extern HANDLE ghInst; #else
extern HINSTANCE ghInst; #endif
///////////////////////////////////////////////////////////////////////////////
// DIB Macros
///////////////////////////////////////////////////////////////////////////////
#define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
#define DibWidthBytes(lpbi) (UINT)WIDTHBYTES((UINT)(lpbi)->biWidth * (UINT)((lpbi)->biBitCount))
#define DibSizeImage(lpbi) ((DWORD)(UINT)DibWidthBytes(lpbi) * (DWORD)(UINT)((lpbi)->biHeight))
#define DibSize(lpbi) ((lpbi)->biSize + (lpbi)->biSizeImage + (int)(lpbi)->biClrUsed * sizeof(RGBQUAD))
#define DibPtr(lpbi) (LPVOID)(DibColors(lpbi) + (UINT)(lpbi)->biClrUsed)
#define DibColors(lpbi) ((LPRGBQUAD)((LPBYTE)(lpbi) + (int)(lpbi)->biSize))
#define DibNumColors(lpbi) ((lpbi)->biClrUsed == 0 && (lpbi)->biBitCount <= 8 \
? (int)(1 << (int)(lpbi)->biBitCount) \ : (int)(lpbi)->biClrUsed)
// !!! Someday write this so you don't have to call ICCompressorChoose if you
// !!! know what you want. Choose would then call this.
// InitCompress(pc, hic/fccHandler, lQuality, lKey, lpbiIn, lpbiOut)
/*****************************************************************************
* @doc EXTERNAL COMPVARS ICAPPS * * @types COMPVARS | This structure describes * compressor when using functions such as <f ICCompressorChoose>, * <f ICSeqCompressFrame>, or <f ICCompressorFree>. * * @field LONG | cbSize | Set this to the size of this structure in bytes. * This member must be set to validate the structure * before calling any function using this structure. * * @field DWORD | dwFlags | Specifies the flags for this structure: * * @flag ICMF_COMPVARS_VALID | Indicates this structure has valid data. * Set this flag if you fill out this structure manually before * calling any functions. Do not set this flag if you let * <f ICCompressorChoose> initialize this structure. * * @field HIC | hic | Specifies the handle of the compressor to use. * The <f ICCompressorChoose> function opens the chosen compressor and * returns the handle to the compressor in this * member. The compressor is closed by <t ICCompressorFree>. * * @field DWORD | fccType | Specifies the type of compressor being used. * Currently only ICTYPE_VIDEO is supported. This can be set to zero. * * @field DWORD | fccHandler | Specifies the four-character code * of the compressor. NULL indicates the data is not * to be recompressed and and 'DIB ' indicates the data is full framed * (uncompressed). You can use this member to specify which * compressor is selected by default when the dialog box is * displayed. * * @field LPBITMAPINFO | lpbiIn | Specifies the input format. Used internally. * * @field LPBITMAPINFO | lpbiOut | Specifies the output format. Ths member * is set by <f ICCompressorChoose>. The <f ICSeqCompressFrameStart> * function uses this member to determine the compressed output format. * If you do not want to use the default format, specify * the preferred one. * * @field LPVOID | lpBitsOut | Used internally for compression. * * @field LPVOID | lpBitsPrev | Used internally for temporal compression. * * @field LONG | lFrame | Used internally to count the number of frames * compressed in a sequence. * * @field LONG | lKey | Set by <f ICCompressorChoose> to indicate the key frame * rate selected in the dialog box. The also specifies the rate that * <f ICSeqCompressFrameStart> uses for making key frames. * * @field LONG | lDataRate | Set by <f ICCompressorChoose> to indicate the * data rate selected in the dialog box. The units are kilobytes per second. * * @field LONG | lQ | Set by <f ICCompressChoose> to indicate the quality * selected in the dialog box. This also specifies the quality * <f ICSeqCompressFrameStart> will use. ICQUALITY_DEFAULT specifies * default quality. * * @field LONG | lKeyCount | Used internally to count key frames. * * @field LPVOID | lpState | Set by <f ICCompressorChoose> to the state selected * in the configuration dialog box for the compressor. The system * uses this information to restore the state of the dialog box if * it is redisplayed. Used internally. * * @field LONG | cbState | Used internally for the size of the state information. * ***************************************************************************/ /*******************************************************************
* @doc EXTERNAL ICCompressorFree ICAPPS * * @api void | ICCompressorFree | This function frees the resources * in the <t COMPVARS> structure used by other IC functions. * * @parm PCOMPVARS | pc | Specifies a pointer to the <t COMPVARS> * structure containing the resources to be freed. * * @comm After using the <f ICCompressorChoose>, <f ICSeqCompressFrameStart>, * <f ICSeqCompressFrame>, and <f ICSeqCompressFrameEnd> functions, call * this function to release the resources in the <t COMPVARS> structure. * * @xref <f ICCompressChoose> <f ICSeqCompressFrameStart> <f ICSeqCompressFrame> * <f ICSeqCompressFrameEnd> * *******************************************************************/ ///////////////////////////////////////////////////////////////////////////////
//
// ICCompressorFree
//
///////////////////////////////////////////////////////////////////////////////
void VFWAPI ICCompressorFree(PCOMPVARS pc) { /* We were passed an invalid COMPPARMS */ if (pc == NULL || pc->cbSize != sizeof(COMPVARS)) return;
// This function frees every thing in the structure (excuse my
// french).
/* Close the compressor */ if (pc->hic) { ICClose(pc->hic); pc->hic = NULL; }
/* Free the output format */ if (pc->lpbiOut) { GlobalFreePtr(pc->lpbiOut); pc->lpbiOut = NULL; }
/* Free the buffer for compressed image */ if (pc->lpBitsOut) { GlobalFreePtr(pc->lpBitsOut); pc->lpBitsOut = NULL; }
/* Free the buffer for the decompressed previous frame */ if (pc->lpBitsPrev) { GlobalFreePtr(pc->lpBitsPrev); pc->lpBitsPrev = NULL; }
/* Free the compressor state buffer */ if (pc->lpState) { GlobalFreePtr(pc->lpState); pc->lpState = NULL; }
/* This structure is no longer VALID */ pc->dwFlags = 0; }
/*******************************************************************
* @doc EXTERNAL ICSeqCompressFrameStart ICAPPS * * @api BOOL | ICSeqCompressFrameStart | This function initializes the system * prior to using <f ICSeqCompressFrame>. * * @parm PCOMPVARS | pc | Specifies a pointer to a <t COMPVARS> structure * initialized with information for compression. * * @parm LPBITMAPINFO | lpbiIn | Specifies the format of the data to be * compressed. * * @rdesc Returns TRUE if successful; otherwise it returns FALSE. * * @comm Prior to using this function, use <f ICCompressorChoose> to let the * user specify a compressor, or initialize a <t COMPVARS> structure * manually. Use <f ICSeqCompressFrameStart>, <f ICSeqCompressFrame> * and <f ICSeqCompressFrameEnd> to compress a sequence of * frames to a specified data rate and number of key frames. * When finished comressing data, use * <f ICCompressorFree> to release the resources * specified in the <t COMPVARS> structure. * * If you do not use <f ICCompressorChoose> you must * initialize the following members of the <t COMPVARS> structure: * * <e COMPVARS.cbSize> Set to the sizeof(COMPVARS) to validate the structure. * * <e COMPVARS.hic> Set to the handle of a compressor you have opened with * <f ICOpen>. You do not need to close it (<f ICCompressorFree> * will do this for you). * * <e COMPVARS.lpbiOut> Optionally set this to force the compressor * to compress to a specific format instead of the default. * This will be freed by <f ICCompressorFree>. * * <e COMPVARS.lKey> Set this to the key-frame frequency you want * or zero for none. * * <e COMPVARS.lQ> Set this to the quality level to use or ICQUALITY_DEFAULT. * * <e COMPVARS.dwFlags> Set ICMF_COMPVARS_VALID flag to indicate the structure is initialized. * * @xref <f ICCompressorChoose> <f ICSeqCompressFrame> <f ICSeqCompressFrameEnd> * <f ICCompressorFree> * *******************************************************************/ ///////////////////////////////////////////////////////////////////////////////
//
// ICSeqCompressFrameStart
//
///////////////////////////////////////////////////////////////////////////////
BOOL VFWAPI ICSeqCompressFrameStart(PCOMPVARS pc, LPBITMAPINFO lpbiIn) { DWORD dwSize; ICINFO icinfo;
if (pc == NULL || pc->cbSize != sizeof(COMPVARS)) return FALSE;
if (pc->hic == NULL || lpbiIn == NULL) return FALSE;
//
// make sure the found compressor can handle something
// if not, force back to the default setting
//
if (ICCompressQuery(pc->hic, lpbiIn, pc->lpbiOut) != ICERR_OK) { // If the input format has changed since the output was selected,
// force a reinitialization of the output format
if (pc->lpbiOut) { GlobalFreePtr (pc->lpbiOut); pc->lpbiOut = NULL; } }
//
// fill in defaults: key frame every frame, and default quality
//
if (pc->lKey < 0) pc->lKey = 1;
if (pc->lQ == ICQUALITY_DEFAULT) pc->lQ = ICGetDefaultQuality(pc->hic);
//
// If no output format is given, use a default
//
if (pc->lpbiOut == NULL) { dwSize = ICCompressGetFormatSize(pc->hic, lpbiIn); if (!dwSize || !(pc->lpbiOut = (LPBITMAPINFO)GlobalAllocPtr(GMEM_MOVEABLE,dwSize))) goto StartError; ICCompressGetFormat(pc->hic, lpbiIn, pc->lpbiOut); } pc->lpbiOut->bmiHeader.biSizeImage = ICCompressGetSize (pc->hic, lpbiIn, pc->lpbiOut); pc->lpbiOut->bmiHeader.biClrUsed = DibNumColors(&(pc->lpbiOut->bmiHeader));
//
// Set the input format and initialize the key frame count
//
pc->lpbiIn = lpbiIn; pc->lKeyCount = pc->lKey; pc->lFrame = 0; // first frame we'll be compressing is 0
if (ICCompressQuery(pc->hic, lpbiIn, pc->lpbiOut) != ICERR_OK) goto StartError;
//
// Allocate a buffer for the compressed bits
//
dwSize = pc->lpbiOut->bmiHeader.biSizeImage;
// !!! Hack for VidCap... make it big enough for two RIFF structs and
// !!! pad records.
//
dwSize += 2048 + 16;
if (!(pc->lpBitsOut = GlobalAllocPtr(GMEM_MOVEABLE, dwSize))) goto StartError;
//
// Allocate a buffer for the decompressed previous frame if it can do
// key frames and we want key frames and it needs such a buffer.
//
ICGetInfo(pc->hic, &icinfo, sizeof(icinfo)); if ((pc->lKey != 1) && (icinfo.dwFlags & VIDCF_TEMPORAL) && !(icinfo.dwFlags & VIDCF_FASTTEMPORALC)) { dwSize = lpbiIn->bmiHeader.biSizeImage; if (!(pc->lpBitsPrev = GlobalAllocPtr(GMEM_MOVEABLE, dwSize))) goto StartError; }
//
// now get compman ready for the big job
//
if (ICCompressBegin(pc->hic, lpbiIn, pc->lpbiOut) != ICERR_OK) goto StartError;
//
// Get ready to decompress previous frames if we're doing key frames
// If we can't decompress, we must do all key frames
//
if (pc->lpBitsPrev) { if (ICDecompressBegin(pc->hic, pc->lpbiOut, lpbiIn) != ICERR_OK) { pc->lKey = pc->lKeyCount = 1; GlobalFreePtr(pc->lpBitsPrev); pc->lpBitsPrev = NULL; } }
return TRUE;
StartError:
// !!! Leave stuff allocated because ICCompressorFree() will clear things
return FALSE; }
/*******************************************************************
* @doc EXTERNAL ICSeqCompressFrameEnd ICAPPS * * @api void | ICSeqCompressFrameEnd | This function terminates sequence * compression using <f ICSeqCompressFrame>. * * @parm PCOMPVARS | pc | Specifies a pointer to a <t COMPVARS> structure * used during sequence compression. * * @comm Use <f ICCompressorChoose> to let the * user specify a compressor to use, or initialize a <t COMPVARS> structure * manually. Use <f ICSeqCompressFrameStart>, <f ICSeqCompressFrame> * and <f ICSeqCompressFrameEnd> functions to compress a sequence of * frames to a specified data rate and number of key frames. When * finished with compression, use <f ICCompressorFree> to * release the resources specified by the <t COMPVARS> structure. * * @xref <f ICCompressorChoose> <f ICSeqCompressFrame> <f ICCompressorFree> * <f ICSeqCompressFrameStart> * *******************************************************************/ ///////////////////////////////////////////////////////////////////////////////
//
// ICSeqCompressFrameEnd
//
///////////////////////////////////////////////////////////////////////////////
void VFWAPI ICSeqCompressFrameEnd(PCOMPVARS pc) { if (pc == NULL || pc->cbSize != sizeof(COMPVARS)) return;
// This function still leaves pc->hic and pc->lpbiOut alloced and open
// since they were set by ICCompressorChoose
// Seems we've already freed everything - don't call ICCompressEnd twice
if (pc->lpBitsOut == NULL) return;
/* Stop compressing */ if (pc->hic) { ICCompressEnd(pc->hic);
if (pc->lpBitsPrev) ICDecompressEnd(pc->hic); }
/* Free the buffer for compressed image */ if (pc->lpBitsOut) { GlobalFreePtr(pc->lpBitsOut); pc->lpBitsOut = NULL; }
/* Free the buffer for the decompressed previous frame */ if (pc->lpBitsPrev) { GlobalFreePtr(pc->lpBitsPrev); pc->lpBitsPrev = NULL; } }
/*******************************************************************
* @doc EXTERNAL ICSeqCompressFrame ICAPPS * * @api LPVOID | ICSeqCompressFrame | This function compresses a * frame in a sequence of frames. The data rate for the sequence * as well as the key-frame frequency can be specified. Use this function * once for each frame to be compressed. * * @parm PCOMPVARS | pc | Specifies a pointer to a <t COMPVARS> structure * initialized with information about the compression. * * @parm UINT | uiFlags | Specifies flags for this function. Set this * parameter to zero. * * @parm LPVOID | lpBits | Specifies a pointer the data bits to compress. * (The data bits excludes header or format information.) * * @parm BOOL FAR * | pfKey | Returns whether or not the frame was compressed * into a keyframe. * * @parm LONG FAR * | plSize | Specifies the maximum size desired for * the compressed image. The compressor might not be able to * compress the data to within this size. When the function * returns, the parameter points to the size of the compressed * image. Images sizes are specified in bytes. * * @rdesc Returns a pointer to the compressed bits. * * @comm Use <f ICCompressorChoose> to let the * user specify a compressor to use, or initialize a <t COMPVARS> structure * manually. Use <f ICSeqCompressFrameStart>, <f ICSeqCompressFrame> * and <f ICSeqCompressFrameEnd> functions to compress a sequence of * frames to a specified data rate and number of key frames. When * finished with compression, use <f ICCompressorFree> to * release the resources specified by the <t COMPVARS> structure. * * Use this function repeatedly to compress a video sequence one * frame at a time. Use this function instead of <f ICCompress> * to compress a video sequence. This function supports creating key frames * in the compressed sequence at any frequency you like and handles * much of the initialization process. * @xref <f ICCompressorChoose> <f ICSeqCompressFrameEnd> <f ICCompressorFree> * <f ICCompressorFreeStart> * *******************************************************************/ ///////////////////////////////////////////////////////////////////////////////
//
// ICSeqCompressFrame
//
// compresses a given image but supports KEY FRAMES EVERY
//
// input:
// pc stuff
// uiFlags flags (not used, must be 0)
// lpBits input DIB bits
// lQuality the reqested compression quality
// pfKey did this frame end up being a key frame?
//
// returns:
// a HANDLE to the converted image. The handle is a DIB in CF_DIB
// format, ie a packed DIB. The caller is responsible for freeing
// the memory. NULL is returned if error.
//
///////////////////////////////////////////////////////////////////////////////
LPVOID VFWAPI ICSeqCompressFrame( PCOMPVARS pc, // junk set up by Start()
UINT uiFlags, // flags
LPVOID lpBits, // input DIB bits
BOOL FAR *pfKey, // did it end up being a key frame?
LONG FAR *plSize) // requested size/size of returned image
{ LONG l; DWORD dwFlags = 0; DWORD ckid = 0; BOOL fKey; LONG lSize = plSize ? *plSize : 0;
// Is it time to make a keyframe?
// First frame will always be a keyframe cuz they initialize to the same
// value.
if (fKey = (pc->lKeyCount >= pc->lKey)) { // For compatibility with existing old apps
dwFlags = AVIIF_KEYFRAME; }
l = ICCompress(pc->hic, fKey ? ICCOMPRESS_KEYFRAME : 0, // flags
(LPBITMAPINFOHEADER)pc->lpbiOut, // output format
pc->lpBitsOut, // output data
(LPBITMAPINFOHEADER)pc->lpbiIn, // format of frame to compress
lpBits, // frame data to compress
&ckid, // ckid for data in AVI file
&dwFlags, // flags in the AVI index.
pc->lFrame, // frame number of seq.
lSize, // reqested size in bytes. (if non zero)
pc->lQ, // quality
fKey ? NULL : (LPBITMAPINFOHEADER)pc->lpbiIn, // fmt of prev frame
fKey ? NULL : pc->lpBitsPrev); // previous frame
if (l < ICERR_OK) goto FrameError;
/* Return the size of the compressed data */ if (plSize) *plSize = pc->lpbiOut->bmiHeader.biSizeImage; // note: we do not reset biSizeImage... despite the fact that we
// allocated this structure, we know its size, and after compression
// the size is wrong!!
/* Now decompress the frame into our buffer for the previous frame */ if (pc->lpBitsPrev) { l = ICDecompress(pc->hic, 0, (LPBITMAPINFOHEADER)pc->lpbiOut, pc->lpBitsOut, (LPBITMAPINFOHEADER)pc->lpbiIn, // !!! should check for this.
pc->lpBitsPrev);
if (l != ICERR_OK) goto FrameError; }
/* Was the compressed image a keyframe? */ *pfKey = (BOOL)(dwFlags & AVIIF_KEYFRAME);
/* After making a keyframe, reset our counter that tells us when we MUST */ /* make another one. */ if (*pfKey) pc->lKeyCount = 0;
// Never make a keyframe again after the first one if we don't want them.
// Increment our counter of how long its been since the last one if we do.
if (pc->lKey) pc->lKeyCount++; else pc->lKeyCount = -1;
// Next time we're called we're on the next frame
pc->lFrame++;
return (pc->lpBitsOut);
FrameError:
return NULL; }
/*******************************************************************
* @doc EXTERNAL ICImageCompress ICAPPS * * @api HANDLE | ICImageCompress | This function provides * convenient method of compressing an image to a given * size. This function does not require use of initialization functions. * * @parm HIC | hic | Specifies the handle to a compressor to * opened with <f ICOpen> or NULL. Use NULL to choose a * default compressor for your compression format. * Applications can use the compressor handle returned * by <f ICCompressorChoose> in the <e COMPVARS.hic> member * of the <t COMPVARS> structure if they want the user to * select the compressor. This compressor is already opened. * * @parm UINT | uiFlags | Specifies flags for this function. Set this * to zero. * * @parm LPBITMAPINFO | lpbiIn | Specifies the input data format. * * @parm LPVOID | lpBits | Specifies a pointer to input data bits to compress. * (The data bits excludes header or format information.) * * @parm LPBITMAPINFO | lpbiOut | Specifies the compressed output format or NULL. * If NULL, the compressor uses a default format. * * @parm LONG | lQuality | Specifies the quality value the compressor. * * @parm LONG FAR * | plSize | Specifies the maximum size desired for * the compressed image. The compressor might not be able to * compress the data to within this size. When the function * returns, the parameter points to the size of the compressed * image. Images sizes are specified in bytes. * * * @rdesc Returns a handle to a compressed DIB. The image data follows the * format header. * * @comm This function returns a DIB with the format and image data. * To obtain the format information from the <t LPBITMAPINFOHEADER> structure, * use <f GlobalLock> to lock the data. Use <f GlobalFree> to free the * DIB when you have finished with it. * * @xref <f ICImageDecompress> * *******************************************************************/ ///////////////////////////////////////////////////////////////////////////////
//
// ICImageCompress
//
// compresses a given image.
//
// input:
// hic compressor to use, if NULL is specifed a
// compressor will be located that can handle the conversion.
// uiFlags flags (not used, must be 0)
// lpbiIn input DIB format
// lpBits input DIB bits
// lpbiOut output format, if NULL is specifed the default
// format choosen be the compressor will be used.
// lQuality the reqested compression quality
// plSize the reqested size for the image/returned size
//
// returns:
// a handle to a DIB which is the compressed image.
//
///////////////////////////////////////////////////////////////////////////////
HANDLE VFWAPI ICImageCompress( HIC hic, // compressor (NULL if any will do)
UINT uiFlags, // flags
LPBITMAPINFO lpbiIn, // input DIB format
LPVOID lpBits, // input DIB bits
LPBITMAPINFO lpbiOut, // output format (NULL => default)
LONG lQuality, // the reqested quality
LONG FAR * plSize) // requested size for compressed frame
{ LONG l; BOOL fNuke; DWORD dwFlags = 0; DWORD ckid = 0; LONG lSize = plSize ? *plSize : 0;
LPBITMAPINFOHEADER lpbi=NULL;
//
// either locate a compressor or use the one supplied.
//
if (fNuke = (hic == NULL)) { hic = ICLocate(ICTYPE_VIDEO, 0L, (LPBITMAPINFOHEADER)lpbiIn, (LPBITMAPINFOHEADER)lpbiOut, ICMODE_COMPRESS);
if (hic == NULL) return NULL; }
//
// make sure the found compressor can compress something ??? WHY BOTHER ???
//
if (ICCompressQuery(hic, lpbiIn, NULL) != ICERR_OK) goto error;
if (lpbiOut) { l = lpbiOut->bmiHeader.biSize + 256 * sizeof(RGBQUAD); } else { //
// now make a DIB header big enough to hold the output format
//
l = ICCompressGetFormatSize(hic, lpbiIn);
if (l <= 0) goto error; }
lpbi = (LPVOID)GlobalAllocPtr(GHND, l);
if (lpbi == NULL) goto error;
//
// if the compressor likes the passed format, use it else use the default
// format of the compressor.
//
if (lpbiOut == NULL || ICCompressQuery(hic, lpbiIn, lpbiOut) != ICERR_OK) ICCompressGetFormat(hic, lpbiIn, lpbi); else hmemcpy(lpbi, lpbiOut, lpbiOut->bmiHeader.biSize + lpbiOut->bmiHeader.biClrUsed * sizeof(RGBQUAD));
lpbi->biSizeImage = ICCompressGetSize(hic, lpbiIn, lpbi); lpbi->biClrUsed = DibNumColors(lpbi);
//
// now resize the DIB to be the maximal size.
//
lpbi = (LPVOID)GlobalReAllocPtr(lpbi,DibSize(lpbi), 0);
if (lpbi == NULL) goto error;
//
// now compress it.
//
if (ICCompressBegin(hic, lpbiIn, lpbi) != ICERR_OK) goto error;
if (lpBits == NULL) lpBits = DibPtr((LPBITMAPINFOHEADER)lpbiIn);
if (lQuality == ICQUALITY_DEFAULT) lQuality = ICGetDefaultQuality(hic);
l = ICCompress(hic, 0, // flags
(LPBITMAPINFOHEADER)lpbi, // output format
DibPtr(lpbi), // output data
(LPBITMAPINFOHEADER)lpbiIn,// format of frame to compress
lpBits, // frame data to compress
&ckid, // ckid for data in AVI file
&dwFlags, // flags in the AVI index.
0, // frame number of seq.
lSize, // requested size in bytes. (if non zero)
lQuality, // quality
NULL, // format of previous frame
NULL); // previous frame
if (l < ICERR_OK) { DPF(("ICCompress returned %ld!\n", l)); ICCompressEnd(hic); goto error; }
// Return the size of the compressed data
if (plSize) *plSize = lpbi->biSizeImage;
if (ICCompressEnd(hic) != ICERR_OK) goto error;
//
// now resize the DIB to be the real size.
//
lpbi = (LPVOID)GlobalReAllocPtr(lpbi, DibSize(lpbi), 0);
//
// all done return the result to the caller
//
if (fNuke) ICClose(hic);
GlobalUnlock(GlobalPtrHandle(lpbi)); return GlobalPtrHandle(lpbi);
error: if (lpbi) GlobalFreePtr(lpbi);
if (fNuke) ICClose(hic);
return NULL; } /*******************************************************************
* * @doc EXTERNAL ICImageDecompress ICAPPS * * @api HANDLE | ICImageDecompress | This function provides * convenient method of decompressing an image without * using initialization functions. ** * @parm HIC | hic | Specifies the handle to a decompressor opened * with <f ICOpen> or NULL. Use NULL to choose a default * decompressor for your format. * * @parm UINT | uiFlags | Specifies flags for this function. Set this * to zero. * * @parm LPBITMAPINFO | lpbiIn | Specifies the compressed input data format. * * @parm LPVOID | lpBits | Specifies a pointer to input data bits to compress. * (The data bits excludes header or format information.) * * @parm LPBITMAPINFO | lpbiOut | Specifies the decompressed output format or NULL. * If NULL, the decompressor uses a default format. * * @rdesc Returns a handle to an uncompressed DIB in the CF_DIB format, * or NULL for an error. The image data follows the format header. * * @comm This function returns a DIB with the format and image data. * To obtain the format information from the <t LPBITMAPINFOHEADER> structure, * use <f GlobalLock> to lock the data. Use <f GlobalFree> to free the * DIB when you have finished with it. *
* @xref <f ICImageCompress> * *******************************************************************/ ///////////////////////////////////////////////////////////////////////////////
//
// ICImageDecompress
//
// decompresses a given image.
//
// input:
// hic compressor to use, if NULL is specifed a
// compressor will be located that can handle the conversion.
// uiFlags flags (not used, must be 0)
// lpbiIn input DIB format
// lpBits input DIB bits
// lpbiOut output format, if NULL is specifed the default
// format choosen be the compressor will be used.
//
// returns:
// a HANDLE to the converted image. The handle is a DIB in CF_DIB
// format, ie a packed DIB. The caller is responsible for freeing
// the memory. NULL is returned if error.
//
///////////////////////////////////////////////////////////////////////////////
HANDLE VFWAPI ICImageDecompress( HIC hic, // compressor (NULL if any will do)
UINT uiFlags, // flags
LPBITMAPINFO lpbiIn, // input DIB format
LPVOID lpBits, // input DIB bits
LPBITMAPINFO lpbiOut) // output format (NULL => default)
{ LONG l; BOOL fNuke; DWORD dwFlags = 0; DWORD ckid = 0;
LPBITMAPINFOHEADER lpbi=NULL;
//
// either locate a compressor or use the one supplied.
//
if (fNuke = (hic == NULL)) { hic = ICLocate(ICTYPE_VIDEO, 0L, (LPBITMAPINFOHEADER)lpbiIn, (LPBITMAPINFOHEADER)lpbiOut, ICMODE_DECOMPRESS);
if (hic == NULL) return NULL; }
//
// make sure the found compressor can decompress at all ??? WHY BOTHER ???
//
if (ICDecompressQuery(hic, lpbiIn, NULL) != ICERR_OK) goto error;
if (lpbiOut) { l = lpbiOut->bmiHeader.biSize + 256 * sizeof(RGBQUAD); } else { //
// now make a DIB header big enough to hold the output format
//
l = ICDecompressGetFormatSize(hic, lpbiIn);
if (l <= 0) goto error; }
lpbi = (LPVOID)GlobalAllocPtr(GHND, l);
if (lpbi == NULL) goto error;
//
// if we didn't provide an output format, use a default.
//
if (lpbiOut == NULL) ICDecompressGetFormat(hic, lpbiIn, lpbi); else hmemcpy(lpbi, lpbiOut, lpbiOut->bmiHeader.biSize + lpbiOut->bmiHeader.biClrUsed * sizeof(RGBQUAD));
//
// For decompress make sure the palette (ie color table) is correct
// just in case they provided an output format and the decompressor used
// that format but not their palette.
//
if (lpbi->biBitCount <= 8) ICDecompressGetPalette(hic, lpbiIn, lpbi);
lpbi->biSizeImage = DibSizeImage(lpbi); // ICDecompressGetSize(hic, lpbi);
lpbi->biClrUsed = DibNumColors(lpbi);
//
// now resize the DIB to be the right size.
//
lpbi = (LPVOID)GlobalReAllocPtr(lpbi,DibSize(lpbi),0);
if (lpbi == NULL) goto error;
//
// now decompress it.
//
if (ICDecompressBegin(hic, lpbiIn, lpbi) != ICERR_OK) goto error;
if (lpBits == NULL) lpBits = DibPtr((LPBITMAPINFOHEADER)lpbiIn);
l = ICDecompress(hic, 0, // flags
(LPBITMAPINFOHEADER)lpbiIn, // format of frame to decompress
lpBits, // frame data to decompress
(LPBITMAPINFOHEADER)lpbi, // output format
DibPtr(lpbi)); // output data
if (l < ICERR_OK) { ICDecompressEnd(hic); goto error; }
if (ICDecompressEnd(hic) != ICERR_OK) goto error;
//
// now resize the DIB to be the real size.
//
lpbi = (LPVOID)GlobalReAllocPtr(lpbi,DibSize(lpbi),0);
//
// all done return the result to the caller
//
if (fNuke) ICClose(hic);
GlobalUnlock(GlobalPtrHandle(lpbi)); return GlobalPtrHandle(lpbi);
error: if (lpbi) GlobalFreePtr(lpbi);
if (fNuke) ICClose(hic);
return NULL; }
///////////////////////////////////////////////////////////////////////////////
//
// ICCompressorChooseStuff
//
///////////////////////////////////////////////////////////////////////////////
INT_PTR VFWAPI ICCompressorChooseDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
typedef struct { DWORD fccType; DWORD fccHandler; UINT uiFlags; LPVOID pvIn; LPVOID lpData; HWND hwnd; HIC hic; LONG lQ; LONG lKey; LONG lDataRate; ICINFO icinfo; LPSTR lpszTitle; PAVISTREAM pavi; AVISTREAMINFOW info; HDRAWDIB hdd; PGETFRAME pgf; LPVOID lpState; LONG cbState; BOOL fClosing; } ICCompressorChooseStuff, FAR *PICCompressorChooseStuff;
/*******************************************************************
* @doc EXTERNAL ICCompressorChoose ICAPPS * * @api BOOL | ICCompressorChoose | Displays a dialog box for choosing a * compressor. It optionally provides a data rate box, key frame box, preview * window, and filtering to display only compressors that can handle a * specific format. * * @parm HWND | hwnd | Specifies the parent window for the dialog box. * * @parm UINT | uiFlags | Specifies flags for this function. The following * flags are defined: * * @flag ICMF_CHOOSE_KEYFRAME | Displays a check box and edit box to enter the * frequency of key frames. * * @flag ICMF_CHOOSE_DATARATE | Displays a check box and edit box to enter the * data rate for the movie. * * @flag ICMF_CHOOSE_PREVIEW | Displays a button to expand the dialog box to * include a preview window. The preview window shows how * frames of your movie will appear when compressed with the * current settings. * * @flag ICMF_CHOOSE_ALLCOMPRESSORS | Indicates all compressors should * should appear in the selection list. If this flag is not specified, * just the compressors that can handle the input format appear in * the selection list. * * @parm LPVOID | pvIn | Specifies the uncompressed * data input format. This parameter is optional. * * @parm LPVOID | lpData | Specifies a <t PAVISTREAM> of type * streamtypeVIDEO to use in the preview window. This parameter * is optional. * * @parm PCOMPVARS | pc | Specifies a pointer to a <t COMPVARS> * structure. The information returned initializes the * structure for use with other functions. * * @parm LPSTR | lpszTitle | Points to a optional zero-terminated string * containing a title for the dialog box. * * @rdesc Returns TRUE if the user chooses a compressor, and presses OK. Returns * FALSE for an error, or if the user presses CANCEL. * * @comm This function lets the user select a compressor from a list. * Before using it, set the <e COMPVARS.cbSize> member of the <t COMPVARS> * structure to sizeof(COMPVARS). Initialize the rest of the structure * to zeros unless you want to specify some valid defaults for * the dialog box. If specifing defaults, set the <e COMPVARS.dwFlags> * member to ICMF_COMPVARS_VALID, and initialize the other members of * the structure. See <f ICSeqCompressorFrameStart> and <t COMPVARS> * for more information about initializing the structure. * * @xref <f ICCompressorFree> <f ICSeqCompressFrameStart> <f ICSeqCompressFrame> * <f ICSeqCompressFrameEnd> * *******************************************************************/ ///////////////////////////////////////////////////////////////////////////////
//
// ICCompressorChoose
//
// Brings up a dialog and allows the user to choose a compression
// method and a quality level, and/or a key frame frequency.
// All compressors in the system are displayed or can be optionally
// filtered by "ability to compress" a specifed format.
//
// the dialog allows the user to configure or bring up the compressors
// about box.
//
// A preview window can be provided to show a preview of a specific
// compression.
//
// the selected compressor is opened (via ICOpen) and returned to the
// caller, it must be disposed of by calling ICCompressorFree.
//
// input:
// HWND hwnd parent window for dialog box.
// UINT uiFlags flags
// LPVOID pvIn input format (optional), only compressors that
// handle this format will be displayed.
// LPVOID pavi input stream for the options preview
// PCOMPVARS pcj returns COMPVARS struct for use with other APIs
// LPSTR lpszTitle Optional title for dialog box
//
// returns:
// TRUE if dialog shown and user chose a compressor.
// FALSE if dialog was not shown or user hit cancel.
//
///////////////////////////////////////////////////////////////////////////////
BOOL VFWAPI ICCompressorChoose( HWND hwnd, // parent window for dialog
UINT uiFlags, // flags
LPVOID pvIn, // input format (optional)
LPVOID pavi, // input stream (for preview - optional)
PCOMPVARS pcj, // state of compressor/dlg
LPSTR lpszTitle) // dialog title (if NULL, use default)
{ INT_PTR f; PICCompressorChooseStuff p; DWORD dwSize;
if (pcj == NULL || pcj->cbSize != sizeof(COMPVARS)) return FALSE;
//
// !!! Initialize the structure - unless the user has already done it
//
if (!(pcj->dwFlags & ICMF_COMPVARS_VALID)) { pcj->hic = NULL; pcj->fccType = 0; pcj->fccHandler = 0; pcj->lQ = ICQUALITY_DEFAULT; pcj->lKey = -1; // means default
pcj->lDataRate = 300; pcj->lpbiOut = NULL; pcj->lpBitsOut = NULL; pcj->lpBitsPrev = NULL; pcj->dwFlags = 0; pcj->lpState = NULL; pcj->cbState = 0; }
// Default type is a video compressor
if (pcj->fccType == 0) pcj->fccType = ICTYPE_VIDEO;
p = (LPVOID)GlobalAllocPtr(GHND, sizeof(ICCompressorChooseStuff));
if (p == NULL) return FALSE;
p->fccType = pcj->fccType; p->fccHandler = pcj->fccHandler; p->uiFlags = uiFlags; p->pvIn = pvIn; p->lQ = pcj->lQ; p->lKey = pcj->lKey; p->lDataRate = pcj->lDataRate; p->lpszTitle = lpszTitle; p->pavi = (PAVISTREAM)pavi; p->hdd = NULL; p->lpState = pcj->lpState; pcj->lpState = NULL; // so it won't be freed
p->cbState = pcj->cbState; // !!! Validate this pointer
// !!! AddRef if it is
if (p->pavi) { if (p->pavi->lpVtbl->Info(p->pavi, &p->info, sizeof(p->info)) != AVIERR_OK || p->info.fccType != streamtypeVIDEO) p->pavi = NULL; }
f = DialogBoxParam(ghInst, TEXT("ICCDLG"), hwnd, ICCompressorChooseDlgProc, (LPARAM)(LPVOID)p);
// !!! Treat error like cancel
if (f == -1) f = FALSE;
//
// if the user picked a compressor then return this info to the caller
//
if (f) {
// If we are called twice in a row, we have good junk in here that
// needs to be freed before we tromp over it.
ICCompressorFree(pcj);
pcj->lQ = p->lQ; pcj->lKey = p->lKey; pcj->lDataRate = p->lDataRate; pcj->hic = p->hic; pcj->fccHandler = p->fccHandler; pcj->lpState = p->lpState; pcj->cbState = p->cbState;
pcj->dwFlags |= ICMF_COMPVARS_VALID; }
GlobalFreePtr(p);
if (!f) return FALSE;
if (pcj->hic && pvIn) { // hic is NULL if no compression selected
/* Get the format we're going to compress into. */ dwSize = ICCompressGetFormatSize(pcj->hic, pvIn); if (!dwSize || ((pcj->lpbiOut = (LPBITMAPINFO)GlobalAllocPtr(GMEM_MOVEABLE, dwSize)) == NULL)) { ICClose(pcj->hic); // Close this since we're erroring
pcj->hic = NULL; return FALSE; } ICCompressGetFormat(pcj->hic, pvIn, pcj->lpbiOut); }
return TRUE; }
void SizeDialog(HWND hwnd, WORD id) { RECT rc;
GetWindowRect(GetDlgItem(hwnd, id), &rc);
/* First, get rc in Client co-ords */ ScreenToClient(hwnd, (LPPOINT)&rc + 1); rc.top = 0; rc.left = 0;
/* Grow by non-client size */ AdjustWindowRect(&rc, GetWindowLong(hwnd, GWL_STYLE), GetMenu(hwnd) !=NULL);
/* That's the new size for the dialog */ SetWindowPos(hwnd, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); }
void TermPreview(PICCompressorChooseStuff p) { if (p->hdd) DrawDibClose(p->hdd); p->hdd = NULL; }
BOOL InitPreview(HWND hwnd, PICCompressorChooseStuff p) {
p->hdd = DrawDibOpen(); if (!p->hdd) return FALSE;
return TRUE; }
#ifdef SAFETOYIELD
//
// Code to yield while we're not calling GetMessage.
// Dispatch all messages. Pressing ESC or closing aborts.
//
BOOL WinYield(HWND hwnd) { MSG msg; BOOL fAbort=FALSE;
while(/* fWait > 0 && */ !fAbort && PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) fAbort = TRUE; if (msg.message == WM_SYSCOMMAND && (msg.wParam & 0xFFF0) == SC_CLOSE) fAbort = TRUE;
if (msg.hwnd == hwnd) { if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN || msg.message == WM_HSCROLL || msg.message == WM_PARENTNOTIFY || msg.message == WM_LBUTTONDOWN) { PostMessage(hwnd, msg.message, msg.wParam, msg.lParam); return TRUE; } } TranslateMessage(&msg); DispatchMessage(&msg); } return fAbort; } #endif
LONG CALLBACK _loadds PreviewStatusProc(LPARAM lParam, UINT message, LONG l) { TCHAR ach[100], achT[100]; BOOL f; PICCompressorChooseStuff p = (PICCompressorChooseStuff) lParam;
if (message != ICSTATUS_STATUS) { DPF(("Status callback: lParam = %lx, message = %u, l = %lu\n", lParam, message, l)); }
// !!!!
// !!!! Status messages need to be fixed!!!!!!
// !!!!
switch (message) { case ICSTATUS_START: break; case ICSTATUS_STATUS: LoadString (ghInst, ID_FRAMECOMPRESSING, achT, NUMELMS(achT)); wsprintf(ach, achT, GetScrollPos(GetDlgItem(p->hwnd, ID_PREVIEWSCROLL), SB_CTL), l); SetDlgItemText(p->hwnd, ID_PREVIEWTEXT, ach); break; case ICSTATUS_END: break;
case ICSTATUS_YIELD:
break; }
#ifdef SAFETOYIELD
f = WinYield(p->hwnd); #else
f = FALSE; #endif
if (f) { DPF(("Aborting from within status proc!\n")); }
return f; }
void Preview(HWND hwnd, PICCompressorChooseStuff p, BOOL fCompress) { RECT rc; HDC hdc; int pos; HANDLE h; HCURSOR hcur = NULL; LPBITMAPINFOHEADER lpbi, lpbiU, lpbiC = NULL; TCHAR ach[120], achT[100]; LONG lsizeD = 0; LONG lSize; int x;
// Not previewing right now!
if (!p->hdd || !p->pgf) return;
pos = GetScrollPos(GetDlgItem(hwnd, ID_PREVIEWSCROLL), SB_CTL); lpbi = lpbiU = AVIStreamGetFrame(p->pgf, pos); if (!lpbi) return;
//
// What would the image look like compressed?
//
if (fCompress && ((INT_PTR)p->hic > 0)) { LRESULT lRet;
lRet = (LRESULT)ICSetStatusProc(p->hic, 0, (LPARAM)p, PreviewStatusProc); if (lRet != 0) { hcur = SetCursor(LoadCursor(NULL, IDC_WAIT)); } // !!! Gives whole data rate to this stream
// !!! What to do if Rate or Scale is zero?
lSize = (GetDlgItemInt(hwnd, ID_DATARATE, NULL, FALSE) * 1024L) / ((p->info.dwScale && p->info.dwRate) ? (p->info.dwRate / p->info.dwScale) : 1L); h = ICImageCompress(p->hic, 0, (LPBITMAPINFO)lpbi, (LPBYTE)lpbi + lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD), NULL, GetScrollPos(GetDlgItem(hwnd, ID_QUALITY), SB_CTL) * 100, &lSize); if (hcur) SetCursor(hcur); if (h) lpbiC = (LPBITMAPINFOHEADER)GlobalLock(h); // Use the compressed image if we have one.. else use the original frame
if (lpbiC) lpbi = lpbiC; }
//
// If we chose NO COMPRESSION, tell them the size of the data as its
// compressed now. Otherwise, use the size it will become when compressed
// or the full frame size.
//
if (fCompress && (p->hic == 0)) { p->pavi->lpVtbl->Read(p->pavi, pos, 1, NULL, 0, &lsizeD, NULL); } else { lsizeD = (lpbiC ? lpbiC->biSizeImage : lpbiU->biSizeImage); }
hdc = GetDC(GetDlgItem(hwnd, ID_PREVIEWWIN)); GetClientRect(GetDlgItem(hwnd, ID_PREVIEWWIN), &rc);
// Clip regions aren't set up right for windows in a dialog, so make sure
// we'll only paint into the window and not spill around it.
IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
// Now go ahead and draw a miniature frame that preserves the aspect ratio
// centred in our preview window
x = MulDiv((int)lpbi->biWidth, 3, 4); if (x <= (int)lpbi->biHeight) { rc.left = (rc.right - MulDiv(rc.right, x, (int)lpbi->biHeight)) / 2; rc.right -= rc.left; } else { x = MulDiv((int)lpbi->biHeight, 4, 3); rc.top = (rc.bottom - MulDiv(rc.bottom, x, (int)lpbi->biWidth)) / 2; rc.bottom -= rc.top; } DrawDibDraw(p->hdd, hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, lpbi, NULL, 0, 0, -1, -1, 0);
// Print the sizes and ratio for this frame
LoadString (ghInst, ID_FRAMESIZE, achT, NUMELMS(achT)); wsprintf(ach, achT, GetScrollPos(GetDlgItem(hwnd, ID_PREVIEWSCROLL), SB_CTL), lsizeD, lpbiU->biSizeImage, lsizeD * 100 / lpbiU->biSizeImage); SetDlgItemText(hwnd, ID_PREVIEWTEXT, ach); if (lpbiC) GlobalFreePtr(lpbiC); ReleaseDC(GetDlgItem(hwnd, ID_PREVIEWWIN), hdc); }
///////////////////////////////////////////////////////////////////////////////
//
// ICCompressorChooseDlgProc
//
// dialog box procedure for ICCompressorChoose, a pointer to a
// ICCompressorChooseStuff pointer must be passed to initialize this
// dialog.
//
// NOTE: this dialog box procedure does not use any globals
// so I did not bother to _export it or use MakeProcAddress() if
// you change this code to use globals, etc, be aware of this fact.
//
///////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
// THIS IS A HACK. We need to store the HIC for each item in a combo
// box. We also need to store it's index. On NT these two items cannot
// be stored in a DWORD. Hence use a static array to contain the HIC
// elements which can then be referenced by the stored index.
// Look at the GetItemData and SetItemData routines.
#define MAX_COMPRESSORS 100
HIC aHic[MAX_COMPRESSORS]; // Hopefully, noone will have more than this
HIC GetHIC(HWND hwndCB, int index) { index = (int) ComboBox_GetItemData(hwndCB,index); if (index>0 && index < MAX_COMPRESSORS) { return(aHic[index]); } else { return((HIC)-1); } }
#else
#define GetHIC(hwnd,index) ((HIC)LOWORD(ComboBox_GetItemData((hwnd),(index))))
#endif
INT_PTR VFWAPI ICCompressorChooseDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { int i,n; int pos; HWND hwndC; PICCompressorChooseStuff p; HIC hic; BOOL fConfig, fAbout, fQuality, fKey, fDataRate; BOOL fShowKeyFrame, fShowDataRate, fShowPreview; int nSelectMe = -1; TCHAR ach[128], achT[80]; RECT rc; UINT id; HDC hdc; BOOL f = FALSE, fCanDecompress = FALSE; LONG lsize; LPBITMAPINFOHEADER lpbi = NULL; BOOL fStreamIsCompressed = FALSE; HRESULT hr;
p = (PICCompressorChooseStuff)GetWindowLongPtr(hwnd,DWLP_USER);
switch (msg) { case WM_INITDIALOG: #define but &&
#define and &&
#define is ==
#define isnt !=
if (lParam == 0) return FALSE;
SetWindowLongPtr(hwnd,DWLP_USER,lParam); p = (PICCompressorChooseStuff)lParam;
p->hwnd = hwnd; // Let the user change the title of the dialog
if (p->lpszTitle != NULL) SetWindowTextA(hwnd, p->lpszTitle);
#ifdef _WIN32
havifile = GetModuleHandleA("avifil32"); #else
havifile = GetModuleHandleA("avifile"); #endif
if (havifile) { (FARPROC)AVIStreamGetFrameOpen = GetProcAddress((HINSTANCE)havifile, (LPCSTR)"AVIStreamGetFrameOpen"); (FARPROC)AVIStreamGetFrame = GetProcAddress((HINSTANCE)havifile, (LPCSTR)"AVIStreamGetFrame"); (FARPROC)AVIStreamGetFrameClose = GetProcAddress((HINSTANCE)havifile, (LPCSTR)"AVIStreamGetFrameClose"); if (p->pavi) p->pgf = AVIStreamGetFrameOpen(p->pavi, NULL); }
// We weren't passed in an input format but we have a PAVI we
// can get a format from
if (p->pvIn is NULL but p->pavi isnt NULL and p->pgf isnt NULL) {
// We need to nuke pvIn later
f = TRUE;
// Find out if the AVI Stream is compressed or not
p->pavi->lpVtbl->ReadFormat(p->pavi, 0, NULL, &lsize); if (lsize) lpbi = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, lsize); if (lpbi) { hr = p->pavi->lpVtbl->ReadFormat(p->pavi, 0, lpbi, &lsize); if (hr == AVIERR_OK) fStreamIsCompressed = lpbi->biCompression != BI_RGB; GlobalFreePtr(lpbi); }
// Get the decompressed format of the AVI stream
lpbi = AVIStreamGetFrame(p->pgf, 0); if (lpbi) { lsize = lpbi->biSize + lpbi->biClrUsed * sizeof(PALETTEENTRY); p->pvIn = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, lsize); if (p->pvIn) hmemcpy(p->pvIn, lpbi, lsize); } }
//
// now fill the combo box with all compressors
//
hwndC = GetDlgItem(hwnd, ID_COMPRESSOR);
for (i=0; ICInfo(p->fccType, i, &p->icinfo); i++) { hic = ICOpen(p->icinfo.fccType, p->icinfo.fccHandler, ICMODE_COMPRESS);
if (hic) { //
// skip this compressor if it can't handle the
// specifed format and we want to skip such compressors
//
if (!(p->uiFlags & ICMF_CHOOSE_ALLCOMPRESSORS) && p->pvIn != NULL && ICCompressQuery(hic, p->pvIn, NULL) != ICERR_OK) { ICClose(hic); continue; }
//
// find out the compressor name.
//
ICGetInfo(hic, &p->icinfo, sizeof(p->icinfo));
//
// stuff it into the combo box and remember which one it was
//
#if defined _WIN32 && !defined UNICODE
//assert (NUMELMS(ach) >= NUMELMS(p->icinfo.szDescription));
mmWideToAnsi (ach, p->icinfo.szDescription, NUMELMS(p->icinfo.szDescription)); n = ComboBox_AddString(hwndC, ach); #else
n = ComboBox_AddString(hwndC, p->icinfo.szDescription); #endif
#ifdef _WIN32
// Making a LONG out of a hic and an int just won't cut it
// We have to use some auxiliary storage
if (i >= MAX_COMPRESSORS) { #ifdef DEBUG
UINT n = fDebug; fDebug = 1; DPF(("Overwriting array...i==%d\n",i)); DebugBreak(); fDebug = n; #endif
} aHic[i] = hic; ComboBox_SetItemData(hwndC, n, i); #else
ComboBox_SetItemData(hwndC, n, MAKELONG(hic, i)); #endif
// This compressor is the one we want to come up default ?
// Set its state
// !!! Combo Box better not be sorted!
// Convert both to upper case for an insensitive compare
AnsiUpperBuff((LPSTR)&p->icinfo.fccHandler, sizeof(FOURCC)); AnsiUpperBuff((LPSTR)&p->fccHandler, sizeof(FOURCC)); if (p->icinfo.fccHandler == p->fccHandler) { nSelectMe = n; if (p->lpState) ICSetState(hic, p->lpState, p->cbState); } } }
//
// Next add a "No Recompression" item unless they passed in an
// uncompressed format
//
if (fStreamIsCompressed || (p->pvIn && ((LPBITMAPINFOHEADER)p->pvIn)->biCompression != BI_RGB)) { LoadString (ghInst, ID_NOCOMPSTRING, ach, NUMELMS(ach)); n = ComboBox_AddString(hwndC, ach); #ifdef _WIN32
aHic[MAX_COMPRESSORS - 1] = 0; ComboBox_SetItemData(hwndC, n, MAX_COMPRESSORS - 1); #else
ComboBox_SetItemData(hwndC, n, 0); #endif
// Select "No Recompression" as the default if that was the one
// last chosen. This will also be the default choice (0).
if (p->fccHandler == 0) nSelectMe = n; } //
// Now add a "Full Frames (Uncompressed)" item unless we can't
// decompress this format and they don't want all choices anyway
//
if (!(p->uiFlags & ICMF_CHOOSE_ALLCOMPRESSORS) && p->pvIn) { // If it's RGB, of course, just offer the option.
if (((LPBITMAPINFOHEADER)p->pvIn)->biCompression != BI_RGB) { if ((hic = ICLocate(ICTYPE_VIDEO, 0, p->pvIn, NULL, ICMODE_DECOMPRESS)) == NULL) goto SkipFF; else ICClose(hic); } }
LoadString (ghInst, ID_FULLFRAMESSTRING, ach, NUMELMS(ach)); n = ComboBox_AddString(hwndC, ach); #ifdef _WIN32
aHic[MAX_COMPRESSORS - 2] = (HIC)-1; ComboBox_SetItemData(hwndC, n, MAX_COMPRESSORS - 2); #else
ComboBox_SetItemData(hwndC, n, MAKELONG(-1, 0)); #endif
// Select "Full Frames" if that was the last one chosen
// !!! Combo Box better not be sorted!
if (p->fccHandler == comptypeDIB) nSelectMe = n; fCanDecompress = TRUE;
SkipFF: // If we haven't selected anything yet, choose something at random.
if (nSelectMe == -1) nSelectMe = 0;
fShowKeyFrame = p->uiFlags & ICMF_CHOOSE_KEYFRAME; fShowDataRate = p->uiFlags & ICMF_CHOOSE_DATARATE; // Don't show a preview if we can't draw it!
fShowPreview = (p->uiFlags & ICMF_CHOOSE_PREVIEW) && p->pavi && fCanDecompress;
// Hide our secret small place holders
ShowWindow(GetDlgItem(hwnd, ID_CHOOSE_SMALL), SW_HIDE); ShowWindow(GetDlgItem(hwnd, ID_CHOOSE_NORMAL), SW_HIDE); ShowWindow(GetDlgItem(hwnd, ID_CHOOSE_BIG), SW_HIDE);
if (!fShowKeyFrame) { ShowWindow(GetDlgItem(hwnd, ID_KEYFRAME), SW_HIDE); ShowWindow(GetDlgItem(hwnd, ID_KEYFRAMEBOX), SW_HIDE); ShowWindow(GetDlgItem(hwnd, ID_KEYFRAMETEXT), SW_HIDE); }
if (!fShowDataRate) { ShowWindow(GetDlgItem(hwnd, ID_DATARATE), SW_HIDE); ShowWindow(GetDlgItem(hwnd, ID_DATARATEBOX), SW_HIDE); ShowWindow(GetDlgItem(hwnd, ID_DATARATETEXT), SW_HIDE); }
if (!fShowPreview) { ShowWindow(GetDlgItem(hwnd, ID_PREVIEW), SW_HIDE); }
// We start without these
ShowWindow(GetDlgItem(hwnd, ID_PREVIEWWIN), SW_HIDE); ShowWindow(GetDlgItem(hwnd, ID_PREVIEWSCROLL), SW_HIDE); ShowWindow(GetDlgItem(hwnd, ID_PREVIEWTEXT), SW_HIDE);
//
// What size dialog do we need?
//
if (!fShowPreview && (!fShowDataRate || !fShowKeyFrame)) SizeDialog(hwnd, ID_CHOOSE_SMALL); else SizeDialog(hwnd, ID_CHOOSE_NORMAL);
//
// Swap places for KeyFrameEvery and DataRate
//
if (fShowDataRate && !fShowKeyFrame) { GetWindowRect(GetDlgItem(hwnd, ID_KEYFRAME), &rc); ScreenToClient(hwnd, (LPPOINT)&rc); ScreenToClient(hwnd, (LPPOINT)&rc + 1); MoveWindow(GetDlgItem(hwnd, ID_DATARATE), rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE); GetWindowRect(GetDlgItem(hwnd, ID_KEYFRAMEBOX), &rc); ScreenToClient(hwnd, (LPPOINT)&rc); ScreenToClient(hwnd, (LPPOINT)&rc + 1); MoveWindow(GetDlgItem(hwnd, ID_DATARATEBOX), rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE); GetWindowRect(GetDlgItem(hwnd, ID_KEYFRAMETEXT), &rc); ScreenToClient(hwnd, (LPPOINT)&rc); ScreenToClient(hwnd, (LPPOINT)&rc + 1); MoveWindow(GetDlgItem(hwnd, ID_DATARATETEXT), rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); }
//
// Restore the dlg to the settings found in the structure
//
SetScrollRange(GetDlgItem(hwnd, ID_QUALITY), SB_CTL, 0, 100, FALSE); CheckDlgButton(hwnd, ID_KEYFRAMEBOX, (BOOL)(p->lKey != 0)); EnableWindow(GetDlgItem(hwnd, ID_KEYFRAME), (BOOL)(p->lKey != 0)); CheckDlgButton(hwnd, ID_DATARATEBOX, (BOOL)(p->lDataRate)); EnableWindow(GetDlgItem(hwnd, ID_DATARATE), (BOOL)(p->lDataRate)); if (p->lKey == -1) // we haven't chosen a key frame yet. Later
// we'll choose the compressor's default.
SetDlgItemInt(hwnd, ID_KEYFRAME, 0, FALSE); else SetDlgItemInt(hwnd, ID_KEYFRAME, (int)p->lKey, FALSE); SetDlgItemInt(hwnd, ID_DATARATE, (int)p->lDataRate, FALSE); ComboBox_SetCurSel(GetDlgItem(hwnd, ID_COMPRESSOR), nSelectMe); SendMessage(hwnd, WM_COMMAND, GET_WM_COMMAND_MPS(ID_COMPRESSOR, hwndC, CBN_SELCHANGE));
// We alloced this ourselves and need to free it now
if (f && p->pvIn) GlobalFreePtr(p->pvIn);
return TRUE;
case WM_PALETTECHANGED:
// It came from us. Ignore it
if ((HWND)wParam == hwnd) break;
case WM_QUERYNEWPALETTE:
if (!p->hdd) break;
hdc = GetDC(hwnd);
//
// Realize the palette of the first video stream
// !!! If first stream isn't video, we're DEAD!
//
if (f = DrawDibRealize(p->hdd, hdc, FALSE)) InvalidateRect(hwnd, NULL, FALSE);
ReleaseDC(hwnd, hdc);
return f;
case WM_PAINT: if (!p->hdd) break; // Paint everybody else before the Preview window since that'll
// take awhile, and we don't want an ugly window during it.
DefWindowProc(hwnd, msg, wParam, lParam); UpdateWindow(hwnd); Preview(hwnd, p, TRUE); return 0;
case WM_HSCROLL: #ifdef _WIN32
id = GetWindowLong(GET_WM_HSCROLL_HWND(wParam, lParam), GWL_ID); pos = GetScrollPos(GET_WM_HSCROLL_HWND(wParam, lParam), SB_CTL); #else
id = GetWindowWord((HWND)HIWORD(lParam), GWW_ID); pos = GetScrollPos((HWND)HIWORD(lParam), SB_CTL); #endif
switch (GET_WM_HSCROLL_CODE(wParam, lParam)) { case SB_LINEDOWN: pos += 1; break; case SB_LINEUP: pos -= 1; break; case SB_PAGEDOWN: pos += (id == ID_QUALITY) ? 10 : (int)p->info.dwLength / 10; break; case SB_PAGEUP: pos -= (id == ID_QUALITY) ? 10 : (int)p->info.dwLength / 10; break; case SB_THUMBTRACK: case SB_THUMBPOSITION: pos = GET_WM_HSCROLL_POS(wParam, lParam); break; case SB_ENDSCROLL: Preview(hwnd, p, TRUE); // Draw this compressed frame
return TRUE; // don't fall through and invalidate
default: return TRUE; }
if (id == ID_QUALITY) { if (pos < 0) pos = 0; if (pos > (ICQUALITY_HIGH/100)) pos = (ICQUALITY_HIGH/100); SetDlgItemInt(hwnd, ID_QUALITYTEXT, pos, FALSE); SetScrollPos(GET_WM_HSCROLL_HWND(wParam, lParam), SB_CTL, pos, TRUE);
} else if (id == ID_PREVIEWSCROLL) {
// !!! round off !!!
if (pos < (int)p->info.dwStart) pos = (int)p->info.dwStart; if (pos >= (int)p->info.dwStart + (int)p->info.dwLength) pos = (int)(p->info.dwStart + p->info.dwLength - 1); SetScrollPos(GET_WM_HSCROLL_HWND(wParam, lParam), SB_CTL, pos, TRUE);
LoadString (ghInst, ID_FRAME, achT, NUMELMS(achT)); wsprintf(ach, achT, pos); SetDlgItemText(hwnd, ID_PREVIEWTEXT, ach);
//Drawing while scrolling flashes palettes because they aren't
//compressed.
//Preview(hwnd, p, FALSE);
}
break;
case WM_COMMAND: hwndC = GetDlgItem(hwnd, ID_COMPRESSOR); n = ComboBox_GetCurSel(hwndC); #ifdef _WIN32
hic = (n == -1) ? NULL : aHic[ComboBox_GetItemData(hwndC,n)]; #else
hic = (n == -1) ? NULL : (HIC)LOWORD(ComboBox_GetItemData(hwndC,n)); #endif
if (!p->fClosing) p->hic = hic;
switch ((int)GET_WM_COMMAND_ID(wParam, lParam)) { // When data rate box loses focus, update our preview
case ID_DATARATE: if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_KILLFOCUS) Preview(hwnd, p, TRUE); break;
// Enable the "data rate" edit box iff we've checked it
case ID_DATARATEBOX: f = IsDlgButtonChecked(hwnd, ID_DATARATEBOX); EnableWindow(GetDlgItem(hwnd, ID_DATARATE), f); break;
// Enable the "key frame" edit box iff we've checked it
case ID_KEYFRAMEBOX: f = IsDlgButtonChecked(hwnd, ID_KEYFRAMEBOX); EnableWindow(GetDlgItem(hwnd, ID_KEYFRAME), f); break;
case ID_COMPRESSOR: if (GET_WM_COMMAND_CMD(wParam, lParam) != CBN_SELCHANGE) break;
if ((INT_PTR)p->hic > 0) { ICGetInfo(p->hic, &p->icinfo, sizeof(p->icinfo));
fConfig = (BOOL)ICQueryConfigure(p->hic); fAbout = ICQueryAbout(p->hic); fQuality = (p->icinfo.dwFlags & VIDCF_QUALITY) != 0; fKey = (p->icinfo.dwFlags & VIDCF_TEMPORAL) != 0; // if they do quality we fake crunch
fDataRate= (p->icinfo.dwFlags & (VIDCF_QUALITY|VIDCF_CRUNCH)) != 0; // We haven't chosen a key frame rate yet. Use this
// compressor's default.
if (p->lKey == -1) SetDlgItemInt(hwnd, ID_KEYFRAME, (int)ICGetDefaultKeyFrameRate(p->hic), FALSE); } else { fConfig = fAbout = fQuality = fKey = fDataRate = FALSE; }
EnableWindow(GetDlgItem(hwnd, ID_CONFIG), fConfig); EnableWindow(GetDlgItem(hwnd, ID_ABOUT), fAbout); EnableWindow(GetDlgItem(hwnd, ID_QUALITY), fQuality); EnableWindow(GetDlgItem(hwnd, ID_QUALITYLABEL), fQuality); EnableWindow(GetDlgItem(hwnd, ID_QUALITYTEXT), fQuality); EnableWindow(GetDlgItem(hwnd, ID_KEYFRAMEBOX), fKey); EnableWindow(GetDlgItem(hwnd, ID_KEYFRAME), fKey && IsDlgButtonChecked(hwnd, ID_KEYFRAMEBOX)); EnableWindow(GetDlgItem(hwnd, ID_KEYFRAMETEXT), fKey); EnableWindow(GetDlgItem(hwnd, ID_DATARATEBOX), fDataRate); EnableWindow(GetDlgItem(hwnd, ID_DATARATE), fDataRate && IsDlgButtonChecked(hwnd, ID_DATARATEBOX)); EnableWindow(GetDlgItem(hwnd, ID_DATARATETEXT), fDataRate);
if (fQuality) { if (p->lQ == ICQUALITY_DEFAULT && (INT_PTR)p->hic > 0) { SetScrollPos(GetDlgItem(hwnd, ID_QUALITY), SB_CTL, (int)ICGetDefaultQuality(p->hic) / 100, TRUE); } else { SetScrollPos(GetDlgItem(hwnd, ID_QUALITY), SB_CTL, (int)p->lQ / 100, TRUE); }
pos = GetScrollPos(GetDlgItem(hwnd, ID_QUALITY),SB_CTL); SetDlgItemInt(hwnd, ID_QUALITYTEXT, pos, FALSE); }
// redraw with new compressor
Preview(hwnd, p, TRUE);
break;
case ID_CONFIG: if ((INT_PTR)p->hic > 0) { ICConfigure(p->hic, hwnd); Preview(hwnd, p, TRUE); } break;
case ID_ABOUT: if ((INT_PTR)p->hic > 0) ICAbout(p->hic, hwnd); break;
case ID_PREVIEW: ShowWindow(GetDlgItem(hwnd, ID_PREVIEW), SW_HIDE); ShowWindow(GetDlgItem(hwnd, ID_PREVIEWWIN), SW_SHOW); ShowWindow(GetDlgItem(hwnd, ID_PREVIEWSCROLL), SW_SHOW); ShowWindow(GetDlgItem(hwnd, ID_PREVIEWTEXT), SW_SHOW); SizeDialog(hwnd, ID_CHOOSE_BIG); // !!! truncation
SetScrollRange(GetDlgItem(hwnd, ID_PREVIEWSCROLL), SB_CTL, (int)p->info.dwStart, (int)(p->info.dwStart + p->info.dwLength - 1), FALSE); SetScrollPos(GetDlgItem(hwnd, ID_PREVIEWSCROLL), SB_CTL, (int)p->info.dwStart, TRUE); LoadString (ghInst, ID_FRAME, achT, NUMELMS(achT)); wsprintf(ach, achT, p->info.dwStart); SetDlgItemText(hwnd, ID_PREVIEWTEXT, ach); InitPreview(hwnd, p); break;
case IDOK:
// !!! We need to call ICInfo to get the FOURCC used
// in system.ini. Calling ICGetInfo will return the
// FOURCC the compressor thinks it is, which won't
// work.
// Get the HIWORD before we nuke it.
#ifndef _WIN32
i = HIWORD(ComboBox_GetItemData(hwndC, n));
//
// Don't close the current compressor in our CANCEL loop
//
ComboBox_SetItemData(hwndC, n, 0); #else
i = (int) ComboBox_GetItemData(hwndC, n); aHic[i] = 0; #endif
//
// Return the values of the dlg to the caller
//
p->hic = hic;
p->lQ = 100 * GetScrollPos(GetDlgItem(hwnd, ID_QUALITY), SB_CTL);
if (IsDlgButtonChecked(hwnd, ID_KEYFRAMEBOX)) p->lKey = GetDlgItemInt(hwnd, ID_KEYFRAME, NULL, FALSE); else p->lKey = 0;
if (IsDlgButtonChecked(hwnd, ID_DATARATEBOX)) p->lDataRate = GetDlgItemInt(hwnd, ID_DATARATE, NULL, FALSE); else p->lDataRate = 0;
// We've chosen a valid compressor. Do stuff.
if ((INT_PTR)p->hic > 0) {
// !!! We need to call ICInfo to get the FOURCC used
// in system.ini. Calling ICGetInfo will return the
// FOURCC the compressor thinks it is, which won't
// work.
ICInfo(p->fccType, i, &p->icinfo); p->fccHandler = p->icinfo.fccHandler; // identify it
// Free the old state
if (p->lpState) { GlobalFreePtr(p->lpState); p->lpState = NULL; } // Get the new state
p->cbState = ICGetStateSize(p->hic); if (p->cbState) { // Remember it's config state
p->lpState = GlobalAllocPtr(GMEM_MOVEABLE, p->cbState); if (p->lpState) { ICGetState(p->hic, p->lpState, p->cbState); } } } else if ((INT_PTR)p->hic == -1) { // "Full Frames"
p->fccHandler = comptypeDIB; p->hic = 0; } else { // "No Compression"
p->fccHandler = 0L; p->hic = 0; }
// fall through
case IDCANCEL: p->fClosing = TRUE; if (GET_WM_COMMAND_ID(wParam, lParam) == IDCANCEL) p->hic = NULL;
n = ComboBox_GetCount(hwndC); for (i=0; i<n; i++) { #ifdef _WIN32
hic = (HIC) aHic[ComboBox_GetItemData(hwndC, i)]; #else
hic = (HIC) LOWORD(ComboBox_GetItemData(hwndC,i)); #endif
if ((INT_PTR)hic > 0) { ICClose(hic); } }
TermPreview(p); if (p->pgf) AVIStreamGetFrameClose(p->pgf); p->pgf = NULL; EndDialog(hwnd, GET_WM_COMMAND_ID(wParam, lParam) == IDOK); break; } break; }
return FALSE; }
/*****************************************************************************
* * dprintf() is called by the DPF macro if DEBUG is defined at compile time. * * The messages will be send to COM1: like any debug message. To * enable debug output, add the following to WIN.INI : * * [debug] * ICM=1 * ****************************************************************************/
#ifdef DEBUG
#define MODNAME "ICM"
extern char szDebug[]; // in MSVIDEO
static void cdecl dprintf(LPSTR szFormat, ...) { char ach[128];
#ifdef _WIN32
va_list va; if (fDebug == -1) fDebug = mmGetProfileIntA(szDebug,MODNAME, FALSE);
if (!fDebug) return;
va_start(va, szFormat); if (szFormat[0] == '!') ach[0]=0, szFormat++; else wsprintfA(ach, MODNAME ": (tid %x) ", GetCurrentThreadId());
wvsprintfA(ach+lstrlenA(ach),szFormat,va); va_end(va); // lstrcat(ach, "\r\r\n");
#else
if (fDebug == -1) fDebug = GetProfileInt("Debug",MODNAME, FALSE);
if (!fDebug) return;
if (szFormat[0] == '!') ach[0]=0, szFormat++; else lstrcpy(ach, MODNAME ": ");
wvsprintf(ach+lstrlen(ach),szFormat,(LPSTR)(&szFormat+1)); // lstrcat(ach, "\r\r\n");
#endif
OutputDebugStringA(ach); }
#endif
|