windows-nt/Source/XPSP1/NT/multimedia/media/avi/msrle/msrle.c
2020-09-26 16:20:57 +08:00

751 lines
20 KiB
C

/*--------------------------------------------------------------------------*\
| 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