/////////////////////////////////////////////////////////////////////////////// // // 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 #include #include #include #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 , * , or . * * @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 * initialize this structure. * * @field HIC | hic | Specifies the handle of the compressor to use. * The function opens the chosen compressor and * returns the handle to the compressor in this * member. The compressor is closed by . * * @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 . The * 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 to indicate the key frame * rate selected in the dialog box. The also specifies the rate that * uses for making key frames. * * @field LONG | lDataRate | Set by to indicate the * data rate selected in the dialog box. The units are kilobytes per second. * * @field LONG | lQ | Set by to indicate the quality * selected in the dialog box. This also specifies the quality * will use. ICQUALITY_DEFAULT specifies * default quality. * * @field LONG | lKeyCount | Used internally to count key frames. * * @field LPVOID | lpState | Set by 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 structure used by other IC functions. * * @parm PCOMPVARS | pc | Specifies a pointer to the * structure containing the resources to be freed. * * @comm After using the , , * , and functions, call * this function to release the resources in the structure. * * @xref * * *******************************************************************/ /////////////////////////////////////////////////////////////////////////////// // // 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 . * * @parm PCOMPVARS | pc | Specifies a pointer to a 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 to let the * user specify a compressor, or initialize a structure * manually. Use , * and to compress a sequence of * frames to a specified data rate and number of key frames. * When finished comressing data, use * to release the resources * specified in the structure. * * If you do not use you must * initialize the following members of the structure: * * Set to the sizeof(COMPVARS) to validate the structure. * * Set to the handle of a compressor you have opened with * . You do not need to close it ( * will do this for you). * * Optionally set this to force the compressor * to compress to a specific format instead of the default. * This will be freed by . * * Set this to the key-frame frequency you want * or zero for none. * * Set this to the quality level to use or ICQUALITY_DEFAULT. * * Set ICMF_COMPVARS_VALID flag to indicate the structure is initialized. * * @xref * * *******************************************************************/ /////////////////////////////////////////////////////////////////////////////// // // 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 . * * @parm PCOMPVARS | pc | Specifies a pointer to a structure * used during sequence compression. * * @comm Use to let the * user specify a compressor to use, or initialize a structure * manually. Use , * and functions to compress a sequence of * frames to a specified data rate and number of key frames. When * finished with compression, use to * release the resources specified by the structure. * * @xref * * *******************************************************************/ /////////////////////////////////////////////////////////////////////////////// // // 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 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 to let the * user specify a compressor to use, or initialize a structure * manually. Use , * and functions to compress a sequence of * frames to a specified data rate and number of key frames. When * finished with compression, use to * release the resources specified by the structure. * * Use this function repeatedly to compress a video sequence one * frame at a time. Use this function instead of * 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 * * *******************************************************************/ /////////////////////////////////////////////////////////////////////////////// // // 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 or NULL. Use NULL to choose a * default compressor for your compression format. * Applications can use the compressor handle returned * by in the member * of the 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 structure, * use to lock the data. Use to free the * DIB when you have finished with it. * * @xref * *******************************************************************/ /////////////////////////////////////////////////////////////////////////////// // // 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 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 structure, * use to lock the data. Use to free the * DIB when you have finished with it. * * @xref * *******************************************************************/ /////////////////////////////////////////////////////////////////////////////// // // 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 of type * streamtypeVIDEO to use in the preview window. This parameter * is optional. * * @parm PCOMPVARS | pc | Specifies a pointer to a * 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 member of the * 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 * member to ICMF_COMPVARS_VALID, and initialize the other members of * the structure. See and * for more information about initializing the structure. * * @xref * * *******************************************************************/ /////////////////////////////////////////////////////////////////////////////// // // 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 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