|
|
/*--------------------------------------------------------------------------*\
| RLECIF.C - Interface to RLE Comressor | |//@@BEGIN_MSINTERNAL |
| History: | | 01/01/88 toddla Created | | 10/30/90 davidmay Reorganized, rewritten somewhat. | | 07/11/91 dannymi Un-hacked | | 09/15/91 ToddLa Re-hacked | | 09/18/91 DavidMay Separated from RLEC.C | | 06/01/92 ToddLa Moved into a installable compressor | |//@@END_MSINTERNAL |
| | \*--------------------------------------------------------------------------*/ /**************************************************************************
* * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR * PURPOSE. * * Copyright (c) 1991 - 1995 Microsoft Corporation. All Rights Reserved. * **************************************************************************/
//@@BEGIN_MSINTERNAL |
#ifndef _WIN32
#include <win32.h>
#endif
//@@END_MSINTERNAL |
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#ifndef _INC_COMPDDK
#define _INC_COMPDDK 50 /* version number */
#endif
#include <vfw.h>
#include "msrle.h"
#include <stdarg.h>
//@@BEGIN_MSINTERNAL |
#ifdef UNICODE
#include "profile.h" // map to registry for NT
#endif
//@@END_MSINTERNAL |
RLESTATE DefaultRleState = {0, 0, -1, 187, 1500, 4};
#define FOURCC_DIB mmioFOURCC('D','I','B',' ')
#define FOURCC_RLE mmioFOURCC('M','R','L','E') //mmioFOURCC('R','L','E',' ')
#define TWOCC_DIB aviTWOCC('d','b')
#define TWOCC_RLE aviTWOCC('d','c')
#define TWOCC_DIBX aviTWOCC('d','x')
/****************************************************************************
****************************************************************************/
#pragma optimize("", off)
static BOOL NEAR PASCAL IsApp(LPTSTR szApp) { TCHAR ach[128]; int i; HINSTANCE hInstance;
#ifdef _WIN32
hInstance = GetModuleHandle(NULL); #else
_asm mov hInstance,ss #endif
GetModuleFileName(hInstance, ach, sizeof(ach) / sizeof(ach[0]));
for (i = lstrlen(ach); i > 0 && ach[i-1] != '\\' && ach[i-1] != '/' && ach[i] != ':'; i--) ;
return lstrcmpi(ach + i, szApp) == 0; } #pragma optimize("", on)
/*****************************************************************************
****************************************************************************/ //
// RleLoad()
//
void NEAR PASCAL RleLoad() { }
/*****************************************************************************
****************************************************************************/ //
// RleFree()
//
void NEAR PASCAL RleFree() { if (gRgbTol.hpTable) GlobalFreePtr(gRgbTol.hpTable);
gRgbTol.hpTable = NULL; }
/*****************************************************************************
****************************************************************************/
//
// RleOpen() - open a instance of the rle compressor
//
PRLEINST NEAR PASCAL RleOpen() { PRLEINST pri;
//
// VIDEDIT Hack
//
// we dont want to see two "Microsoft RLE" compressors.
// so lie to VidEdit and fail to open.
//
if (GetModuleHandle(TEXT("MEDDIBS")) && IsApp(TEXT("VIDEDIT.EXE"))) return NULL;
pri = (PRLEINST)LocalAlloc(LPTR, sizeof(RLEINST));
if (pri) { RleSetState(pri, NULL, 0); } return pri; }
/*****************************************************************************
****************************************************************************/ //
// RleClose() - close a instance of the rle compressor
//
DWORD NEAR PASCAL RleClose(PRLEINST pri) { if (!pri) return FALSE;
if (pri->lpbiPrev) { GlobalFreePtr(pri->lpbiPrev); pri->lpbiPrev = NULL; }
LocalFree((LOCALHANDLE)pri); return TRUE; }
/*****************************************************************************
****************************************************************************/ //
// RleGetState() - get the current state of the rle compressor
//
// will copy current state into passed buffer.
// returns the size in bytes required to store the entire state.
//
DWORD NEAR PASCAL RleGetState(PRLEINST pri, LPVOID pv, DWORD dwSize) { if (pv == NULL || dwSize == 0) return sizeof(RLESTATE);
if (pri == NULL || dwSize < sizeof(RLESTATE)) return 0;
*(LPRLESTATE)pv = pri->RleState; return sizeof(RLESTATE); }
/*****************************************************************************
****************************************************************************/ //
// RleSetState() - sets the current state of the rle compressor
//
DWORD NEAR PASCAL RleSetState(PRLEINST pri, LPVOID pv, DWORD dwSize) { if (pv == NULL || dwSize == 0) { pv = &DefaultRleState; dwSize = sizeof(RLESTATE); }
if (pri == NULL || dwSize < sizeof(RLESTATE)) return 0;
pri->RleState = *(LPRLESTATE)pv; return sizeof(RLESTATE); }
#if !defined NUMELMS
#define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
#endif
#if defined _WIN32 && !defined UNICODE
int LoadUnicodeString(HINSTANCE hinst, UINT wID, LPWSTR lpBuffer, int cchBuffer) { char ach[128]; int i;
i = LoadString(hinst, wID, ach, sizeof(ach));
if (i > 0) MultiByteToWideChar(CP_ACP, 0, ach, -1, lpBuffer, cchBuffer);
return i; }
#else
#define LoadUnicodeString LoadString
#endif
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleGetInfo(PRLEINST pri, ICINFO FAR *icinfo, DWORD dwSize) { if (icinfo == NULL) return sizeof(ICINFO);
if (dwSize < sizeof(ICINFO)) return 0;
icinfo->dwSize = sizeof(ICINFO); icinfo->fccType = ICTYPE_VIDEO; icinfo->fccHandler = FOURCC_RLE; icinfo->dwFlags = VIDCF_QUALITY | // supports quality
VIDCF_TEMPORAL | // supports inter-frame
VIDCF_CRUNCH; // can crunch to a data rate
icinfo->dwVersion = ICVERSION;
LoadUnicodeString(ghModule, IDS_DESCRIPTION, icinfo->szDescription, NUMELMS(icinfo->szDescription)); LoadUnicodeString(ghModule, IDS_NAME, icinfo->szName, NUMELMS(icinfo->szName));
return sizeof(ICINFO); }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleCompressQuery(PRLEINST pri, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { //
// determine if the input DIB data is in a format we like.
//
if (lpbiIn == NULL || lpbiIn->biBitCount != 8 || lpbiIn->biCompression != BI_RGB) return (DWORD)ICERR_BADFORMAT;
//
// are we being asked to query just the input format?
//
if (lpbiOut == NULL) return ICERR_OK;
//
// make sure we can handle the format to compress to also.
//
if (lpbiOut->biCompression != BI_RLE8 || // must be rle format
lpbiOut->biBitCount != 8 || // must be 8bpp
lpbiOut->biWidth != lpbiIn->biWidth || // must be 1:1 (no stretch)
lpbiOut->biHeight != lpbiIn->biHeight) return (DWORD)ICERR_BADFORMAT;
return ICERR_OK; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleCompressGetFormat(PRLEINST pri, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { DWORD dw; DWORD dwClrUsed;
if (dw = RleCompressQuery(pri, lpbiIn, NULL)) return dw;
dwClrUsed = lpbiIn->biClrUsed; if (dwClrUsed == 0) { dwClrUsed = 256; } dw = lpbiIn->biSize + (int)dwClrUsed * sizeof(RGBQUAD);
//
// if lpbiOut == NULL then, return the size required to hold a output
// format
//
if (lpbiOut == NULL) return dw;
hmemcpy(lpbiOut, lpbiIn, dw);
lpbiOut->biBitCount = 8; lpbiOut->biCompression = BI_RLE8; lpbiOut->biSizeImage = RleCompressGetSize(pri, lpbiIn, lpbiOut);
return ICERR_OK; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleCompressBegin(PRLEINST pri, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { DWORD dw;
if (dw = RleCompressQuery(pri, lpbiIn, lpbiOut)) return dw;
if (pri->lpbiPrev) { GlobalFreePtr(pri->lpbiPrev); pri->lpbiPrev = NULL; }
pri->iStart = 0; pri->lLastParm = 0L;
pri->fCompressBegin = TRUE;
MakeRgbTable(lpbiIn);
return ICERR_OK; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleCompressGetSize(PRLEINST pri, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { int dx,dy;
//
// we assume RLE data will never be twice the size of a full frame.
//
dx = (int)lpbiIn->biWidth; dy = (int)lpbiIn->biHeight;
return (DWORD)(UINT)dx * (DWORD)(UINT)dy * 2; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleCompress(PRLEINST pri, ICCOMPRESS FAR *icinfo, DWORD dwSize) { DWORD dw; BOOL fFrameHalvingOccurred = FALSE;
LPBITMAPINFOHEADER lpbi;
if (!pri->fCompressBegin) { if (dw = RleCompressBegin(pri, icinfo->lpbiInput, icinfo->lpbiOutput)) return dw;
pri->fCompressBegin = FALSE; }
//
// we can compress in one of two ways:
//
// if a frame size is given (>0) then call CrunchDib using the passed
// quality as the "frame half" setting.
//
// if a frame size is not given (==0) then use the passed quality
// as the tolerance and do a normal RleDeltaFrame()
//
if (icinfo->dwQuality == ICQUALITY_DEFAULT) icinfo->dwQuality = QUALITY_DEFAULT;
if (icinfo->dwFrameSize > 0) { dw = ICQUALITY_HIGH - icinfo->dwQuality;
pri->RleState.lMaxFrameSize = icinfo->dwFrameSize; pri->RleState.lMinFrameSize = icinfo->dwFrameSize - 500; pri->RleState.tolMax = dw; pri->RleState.tolSpatial = dw / 8; pri->RleState.tolTemporal = ADAPTIVE;
// SplitDib makes really ugly artifacts by splitting the frame into who knows
// how many pieces which will be pieced together like a bad jigsaw puzzle where
// each piece is from a different picture. I decided never to use this method
// of compression.
#if 0
if (dw == 0) { pri->RleState.tolSpatial = 0; pri->RleState.tolTemporal = 0;
SplitDib(pri, icinfo->lpbiOutput, icinfo->lpOutput, icinfo->lpbiPrev, icinfo->lpPrev, icinfo->lpbiInput, icinfo->lpInput); } else #endif
{ CrunchDib(pri, icinfo->lpbiOutput, icinfo->lpOutput, icinfo->lpbiPrev, icinfo->lpPrev, icinfo->lpbiInput, icinfo->lpInput); }
lpbi = icinfo->lpbiOutput;
if (lpbi->biCompression == BI_DIBX) fFrameHalvingOccurred = TRUE;
if (icinfo->lpckid) { if (fFrameHalvingOccurred) *icinfo->lpckid = TWOCC_DIBX; else *icinfo->lpckid = TWOCC_RLE; }
lpbi->biCompression = BI_RLE8; // biSizeImage is filled in
} else { dw = ICQUALITY_HIGH - icinfo->dwQuality;
pri->RleState.tolSpatial = dw; pri->RleState.tolTemporal = dw / 8;
RleDeltaFrame( icinfo->lpbiOutput, icinfo->lpOutput, icinfo->lpbiPrev, icinfo->lpPrev, icinfo->lpbiInput, icinfo->lpInput, 0,-1, pri->RleState.tolTemporal, pri->RleState.tolSpatial, pri->RleState.iMaxRunLen,4);
if (icinfo->lpckid) *icinfo->lpckid = TWOCC_RLE; }
//
// set the AVI index flags,
//
// make it a keyframe, if no previous frame
//
if (icinfo->lpdwFlags) { if (icinfo->lpbiPrev == NULL && !fFrameHalvingOccurred) *icinfo->lpdwFlags |= AVIIF_TWOCC | AVIIF_KEYFRAME; else *icinfo->lpdwFlags |= AVIIF_TWOCC; }
return ICERR_OK; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleCompressEnd(PRLEINST pri) { pri->fCompressBegin = FALSE; return ICERR_OK; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleDecompressQuery(RLEINST * pri, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { //
// determine if the input DIB data is in a format we like.
// We like all RGB. We like 8bit RLE.
//
if (lpbiIn == NULL || (lpbiIn->biBitCount != 8 && lpbiIn->biCompression == BI_RLE8) || (lpbiIn->biCompression != BI_RGB && lpbiIn->biCompression != BI_RLE8)) return (DWORD)ICERR_BADFORMAT;
//
// are we being asked to query just the input format?
//
if (lpbiOut == NULL) return ICERR_OK;
//
// make sure we can handle the format to decompress too.
//
if (lpbiOut->biCompression != BI_RGB || // must be full dib
lpbiOut->biBitCount != lpbiIn->biBitCount ||// must match
lpbiOut->biWidth != lpbiIn->biWidth || // must be 1:1 (no stretch)
lpbiOut->biHeight != lpbiIn->biHeight) return (DWORD)ICERR_BADFORMAT;
return ICERR_OK; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleDecompressGetFormat(RLEINST * pri, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { DWORD dw;
if (dw = RleDecompressQuery(pri, lpbiIn, NULL)) return dw;
dw = lpbiIn->biSize + (int)lpbiIn->biClrUsed * sizeof(RGBQUAD);
//
// if lpbiOut == NULL then, return the size required to hold a output
// format
//
if (lpbiOut == NULL) return dw;
hmemcpy(lpbiOut, lpbiIn, dw);
lpbiOut->biBitCount = lpbiIn->biBitCount; lpbiOut->biCompression = BI_RGB; lpbiOut->biSizeImage = lpbiIn->biHeight * DibWidthBytes(lpbiIn);
return ICERR_OK; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleDecompressBegin(RLEINST * pri, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut) { DWORD dw;
if (dw = RleDecompressQuery(pri, lpbiIn, lpbiOut)) return dw;
pri->fDecompressBegin = TRUE;
// Make sure we know the size of an uncompressed DIB
if (lpbiOut->biSizeImage == 0) lpbiOut->biSizeImage = lpbiOut->biHeight * DibWidthBytes(lpbiOut);
return ICERR_OK; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleDecompress(RLEINST * pri, ICDECOMPRESS FAR *icinfo, DWORD dwSize) { DWORD dw;
if (!pri->fDecompressBegin) { if (dw = RleDecompressBegin(pri, icinfo->lpbiInput, icinfo->lpbiOutput)) return dw;
pri->fDecompressBegin = FALSE; }
//
// handle a decompress of 'DIB ' (ie full frame) data. Just return it.
// It may be disguised an an RLE. We can tell by how big it is
//
if (icinfo->lpbiInput->biCompression == BI_RGB || icinfo->lpbiInput->biSizeImage == icinfo->lpbiOutput->biSizeImage) { hmemcpy(icinfo->lpOutput, icinfo->lpInput, icinfo->lpbiInput->biSizeImage); return ICERR_OK; }
DecodeRle(icinfo->lpbiOutput, icinfo->lpOutput, icinfo->lpInput, icinfo->lpbiInput->biSizeImage);
return ICERR_OK; }
/*****************************************************************************
****************************************************************************/ DWORD NEAR PASCAL RleDecompressEnd(RLEINST * pri) { pri->fDecompressBegin = FALSE; return ICERR_OK; }
/***************************************************************************
DecodeRle - 'C' version
Play back a RLE buffer into a DIB buffer
returns none
***************************************************************************/
void NEAR PASCAL DecodeRle(LPBITMAPINFOHEADER lpbi, LPVOID lp, LPVOID lpRle, DWORD dwInSize) { UINT cnt; BYTE b; UINT x; UINT dx,dy; UINT wWidthBytes; DWORD dwOutSize; DWORD dwJump;
#define RLE_ESCAPE 0
#define RLE_EOL 0
#define RLE_EOF 1
#define RLE_JMP 2
#define RLE_RUN 3
#ifndef _WIN32
extern FAR PASCAL __WinFlags; #define WinFlags (UINT)(&__WinFlags)
//
// this uses ASM code found in RLEA.ASM
//
if (!(WinFlags & WF_CPU286)) DecodeRle386(lpbi, lp, lpRle); else if (lpbi->biSizeImage < 65536l) DecodeRle286(lpbi, lp, lpRle); else #endif
{ BYTE _huge *pb = lp; BYTE _huge *prle = lpRle;
#define EatOutput(_x_) \
{ \ if (dwOutSize < (_x_)) { \ return; \ } \ dwOutSize -= (_x_); \ } #define EatInput(_x_) \
{ \ if (dwInSize < (_x_)) { \ return; \ } \ dwInSize -= (_x_); \ }
if (lpbi->biHeight <= 0) { return; } wWidthBytes = (UINT)lpbi->biWidth+3 & ~3; dwOutSize = wWidthBytes * (DWORD)lpbi->biHeight;
x = 0;
for (;;) { EatInput(2); cnt = (UINT)*prle++; b = *prle++;
if (cnt == RLE_ESCAPE) { switch (b) { case RLE_EOF: return;
case RLE_EOL: EatOutput(wWidthBytes - x); pb += wWidthBytes - x; x = 0; break;
case RLE_JMP: EatInput(2); dx = (UINT)*prle++; dy = (UINT)*prle++;
dwJump = (DWORD)wWidthBytes * dy + dx; EatOutput(dwJump); pb += dwJump; x += dx;
break;
default: cnt = b; EatOutput(cnt); EatInput(cnt); x += cnt; // If the count was sufficiently large it would be worthwhile
// using an inline memcpy function. The code could
// be faster. Even doing this as a series of word
// moves would be quicker. However, RLE is not the highest
// priority.
while (cnt-- > 0) *pb++ = *prle++; // copy
if (b & 1) { EatInput(1); prle++; }
break; } } else { x += cnt;
// If the count was sufficiently large it would be worthwhile
// using an inline memset function. The code could
// be faster. Even doing this as a series of word
// moves would be quicker. However, RLE is not the highest
// priority.
#if 1
// at least on the x86... this way persuades the compiler
// to use registers more effectively through the whole of
// the decode routine
EatOutput(cnt); while (cnt-- > 0) { *pb++ = b; // set
}
#else // the alternative
memset(pb, b, cnt); pb += cnt; #endif
} } } }
#ifdef DEBUG
void FAR cdecl dprintf(LPSTR szFormat, ...) { char ach[256]; va_list va;
static BOOL fDebug = -1;
if (fDebug == -1) fDebug = GetProfileIntA("Debug", "MSRLE", FALSE);
if (!fDebug) return;
lstrcpyA(ach, "MSRLE: "); va_start(va, szFormat); wvsprintfA(ach+7, szFormat, va); va_end(va); lstrcatA(ach, "\r\n");
OutputDebugStringA(ach); }
#endif
|