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

451 lines
14 KiB
C

/*--------------------------------------------------------------------------*\
| RLEC.C - MS-CRUNCH |
|//@@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 |
|//@@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.
*
**************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include "msrle.h"
#ifdef _WIN32
#define _huge
#endif
//
// make a copy of a DIB that is not packed.
//
__inline static LPVOID CopyDib(LPBITMAPINFOHEADER lpbi, LPVOID lpS)
{
LPVOID lpD;
BYTE _huge *s;
BYTE _huge *d;
long lImageHeader, lImageData, lImageSize;
if (!lpbi || !lpS)
return NULL;
FixBitmapInfo(lpbi);
lpD = GlobalAllocPtr(GHND, lImageSize = DibSize(lpbi));
if (lpD)
{
// Copy the bitmapinfoheader and colours
s = (LPVOID)lpbi;
d = (LPVOID)lpD;
lImageData = DibSizeImageX(lpbi); // grab the number of data bytes
lImageHeader = lImageSize - lImageData; // save header+colortable size
#if 0
while (lImageHeader-- > 0)
*d++ = *s++;
#else
memcpy(d, s, lImageHeader); // copy the header+colortable to new Dib
d += lImageHeader; // step pointer to Data piece
#endif
// Copy the image
s = (LPVOID)lpS;
#if 0
while (lImageData-- > 0)
*d++ = *s++;
#else
memcpy(d, s, lImageData); // copy data bytes to new Dib
#endif
}
return lpD;
}
//
// CrunchDib() - make a DIB fit into a specific size.
//
BOOL FAR PASCAL CrunchDib(PRLEINST pri,
LPBITMAPINFOHEADER lpbiRle, LPBYTE lpRle,
LPBITMAPINFOHEADER lpbiFrom,LPBYTE lpFrom,
LPBITMAPINFOHEADER lpbiTo, LPBYTE lpTo)
{
long dwSize = 0L, dwLastSize = 0L;
long lCurParm = 0L;
long lTempMax; // highest value before halving
long tolMax;
long lTempMin = 0L;
BOOL fInterlaceNow = FALSE; // time to try interlacing?
long lBumpUp = 2048L; // bump the parameter up by this amount
int iStart, iLen;
BOOL fSpatialAdaptive;
BOOL fTemporalAdaptive;
long tolTemporal;
long tolSpatial;
int minJump;
int maxRun;
int FIRSTTRY = 1024; // use this parameter value as a first guess
int CWND = 250; // Give up searching for the perfect parameter
// when the window is smaller than this
lTempMax = pri->RleState.tolMax; // highest value before halving
// No Previous DIB -- we want a full frame, so no interlacing allowed
// (infinite tolerance allowed before frame halving)
if (lpbiFrom == NULL)
lTempMax = MAXTOL;
// In case we were passed a bogus value -- don't allow frame halving at all
if (lTempMax < 0)
lTempMax = MAXTOL;
tolMax = lTempMax;
tolTemporal = pri->RleState.tolTemporal;
tolSpatial = pri->RleState.tolSpatial;
fSpatialAdaptive = (pri->RleState.tolSpatial == ADAPTIVE);
fTemporalAdaptive = (pri->RleState.tolTemporal == ADAPTIVE);
maxRun = pri->RleState.iMaxRunLen;
minJump = 4;
// No Previous DIB - we should do a full frame, so no interlacing and
// allow spatial compression to be adaptive to do the compression since
// we can't do temporal compression.
if (lpbiFrom == NULL) {
pri->iStart = 0;
fSpatialAdaptive = TRUE;
}
iStart = pri->iStart;
iLen = -1;
if (!lpbiTo) {
DPF(("Crunch Error - Invalid DIB or HPAL"));
goto return_failure;
}
//
// In the previous frame, we did the bottom only,
// so now we need to do the top
//
// If lpbiFrom is NULL, we don't want to do this--we want to make
// a full frame, even though the last one was a first half.
//
if (iStart > 0 && pri->lpbiPrev) {
fInterlaceNow = TRUE; // Only do half of the frame.
lpbiTo = pri->lpbiPrev;
lpTo = DibPtr(lpbiTo); // This will be a packed DIB
lTempMin = 0L;
lTempMax = MAXTOL; // no limit to how fuzzy you can get before
tolMax = MAXTOL; // interlacing since we already are doing it
DPF(("SECOND HALF OF INTERLACE"));
//
// copy over the color table from the last DIB to the empty RLE
// to delay any palette change....
//
hmemcpy(lpbiRle,lpbiTo,lpbiTo->biSize+(int)lpbiTo->biClrUsed*sizeof(RGBQUAD));
} else {
iStart = 0;
}
// OK. Here's where we work on getting the frame down in size!
// First, try an EXACT RLE with no fuzziness. If that works, no need to degrade
// the image quality at all!
if (!RleDeltaFrame(lpbiRle,lpRle,lpbiFrom,lpFrom,lpbiTo,lpTo,iStart,iLen,0L,0L,0,0)) {
DPF(("Crunch Error - Lossless RleDeltaFrame failed"));
goto return_failure;
}
dwSize = lpbiRle->biSizeImage;
DPF(("tolTemporal = 0, tolSpatial = 0, Size = %ld", dwSize));
// Exact RLE worked!
if (dwSize < pri->RleState.lMaxFrameSize) {
if (fInterlaceNow)
pri->iStart = 0; // we did 2nd half, so next time do full dib
goto return_success;
}
if (pri->lLastParm) // this value worked last time, so try it now!
// unless of course, it's too big.
lCurParm = min(pri->lLastParm, lTempMax);
else if (lTempMax == MAXTOL) // no limit to what parameter can be
lCurParm = FIRSTTRY; // so make the 1st value reasonable
else
lCurParm = lTempMax; // There is a limit on how big the parm can be.
// Start as big as possible, so that if that
// doesn't fit, we can give up right away
goto skip_if; // skip the big IF
noskip_if:
// This first condition tests to see if the current attempt yielded a frame
// that was still too big, and we have just tried the largest parameter
// possible. It looks like we will never get the frame small enough!
// Our only hope is to interlace the frames, if we're allowed to.
if (dwSize > pri->RleState.lMaxFrameSize && lCurParm > tolMax-1)
{
// It looks like either we're a keyframe and can't interlace, or
// we've been trying interlacing and we're STILL not small enough.
// There is nothing else we can do. Give up.
// NOTE: this shouldn't happen if the parameter is allowed to grow
// arbitrarily!
if (fInterlaceNow || !lpbiFrom) {
if (!lpbiFrom)
goto return_success;
if (iStart > 0) { // This was 2nd frame of a pair (top)
pri->iStart = 0;
lCurParm = 0L; // don't remember this value because
// this frame halving value won't help
// us next frame when we aren't using
// frame halving any more.
} else { // This was the first frame of a pair (bottom).
// Remember to do the 2nd frame next time
pri->iStart += iLen;
}
goto return_success;
// We are allowed to interlace, so we can prepare to.
// Gee, I hope this isn't the last frame in the movie
// (there will be no frame to do the 2nd half of!! )
} else {
fInterlaceNow = TRUE;
DPF(("FIRST HALF OF INTERLACE"));
iStart = 0;
iLen = (int)lpbiTo->biHeight/2;
lCurParm = 0L; // start with no fuzziness
lTempMin = 0L;
lTempMax = MAXTOL; // no limit to fuzziness
tolMax = MAXTOL;
}
// This condition tests to see if the size is still too big after this attempt,
// and the window of parameter values that we can try is still large enough
// to try some more values. If so, we shrink the window a bit (the new lowest
// value worth trying is the current value, and we bump the current value up by
// half of the window size, but not TOO much. You see, if our parameter is too
// high, then we binary search smaller values between 0 and this value. But if
// the parameter is too small, how do we binary search through here and
// infinity? (actually 195,075) So, we just increase the parameter by 2048.
// Next time we need to increase it, we will increase by 4096, 8192, etc.
// This way, we will quickly get to the limit of 195,075. Perhaps the frame
// cannot possibly be crunched as small as it needs to be. The program
// shouldn't take forever to realize this and get to 195,075. But we shouldn't
// binary search between 0 and 195,075 because it will waste time getting down
// to the small values like 1000 that most movies will need. This is the
// best compromise. Hope that wasn't too long winded! :-)
} else if ((dwSize > pri->RleState.lMaxFrameSize) &&
((lTempMax - lTempMin) > CWND))
{
lTempMin = lCurParm;
if (lTempMax == MAXTOL){ // upper limit is still unbounded so
// leap way higher to our next try
if (MAXTOL - lCurParm < lBumpUp)
lCurParm = MAXTOL;
else
lCurParm += lBumpUp;
lBumpUp *= 2;
} else
lCurParm += (lTempMax - lCurParm) >> 1;
// For this condition, we are still too big, but the window is getting so small
// that we fear we will never find a value that works! Let's say we know that
// 200 gives a frame that is too big, and 210 gives a frame that is too small.
// Should we bother searching any more? NO!!! That would waste time. Let's
// just give up and take the 210 value (too small is better than too large)
// and continue. The next time through this loop, it will give up when it sees
// that the window is too small and the current attempt produced a frame that
// was small enough, even though it was a little smaller than we wanted.
} else if (dwSize > pri->RleState.lMaxFrameSize) {
lCurParm = lTempMax;
// This condtion says that the size is too small to accept, and the window
// of values to try is still large enough to warrant trying again. So, we
// close the window a bit by setting the new highest value worth trying to
// the current value, and dropping the current value by half.
} else if ((dwSize < pri->RleState.lMinFrameSize) && ((lTempMax - lTempMin) > CWND)) {
lTempMax = lCurParm;
lCurParm -= (lCurParm - lTempMin) >> 1;
// Here is the catch all last else of the if. If it gets here, then the frame
// is either just the perfect size and we can quit, or it's too small, but
// we've determined that we can't be bothered to search any more, so we're going
// to quit anyway.
} else {
if (fInterlaceNow) { // we were interlacing
if (iStart > 0) { // this was 2nd half of a pair (top)
pri->iStart = 0;
lCurParm = 0L; // don't remember this value because
// this frame halving value won't help
// us next frame when we aren't using
// frame halving any more.
} else { // This was 1st half of a pair (bottom)
pri->iStart = iLen; // next time, do 2nd half
}
}
goto return_success;
}
skip_if:
// We know that the previous attempt to RLE didn't work, so try again with
// the new values.
Yield();
// Set the TEMPORAL and SPATIAL values.
// NOTE: if we are only working with a single DIB, (no lpbiFrom),
// TEMPORAL compression won't work, so we enabled SPATIAL adaptive.
// The TEMPORAL value will be ignored in that case.
if (fSpatialAdaptive && fTemporalAdaptive) {
tolSpatial = lCurParm>>3; // lCurParm/8;
tolTemporal = lCurParm;
} else if (fTemporalAdaptive)
tolTemporal = lCurParm;
else if (fSpatialAdaptive)
tolSpatial = lCurParm;
if (!RleDeltaFrame(lpbiRle,lpRle,lpbiFrom,lpFrom,lpbiTo,lpTo,iStart,iLen,tolTemporal,tolSpatial,maxRun,minJump)) {
DPF(("Crunch Error - Rle Delta Frame failed"));
goto return_failure;
}
// Remember the size of the last attempt, and take size of this attempt
dwLastSize = dwSize;
dwSize = lpbiRle->biSizeImage;
DPF(("tolTemporal=%ld, tolSpatial=%ld, Size=%ld", tolTemporal, tolSpatial, dwSize));
goto noskip_if; // Go back and see how we did!
return_failure:
pri->lLastParm = 0L;
return FALSE;
return_success:
// if (lCurParm) // putting this line in won't let frame halving
// threshold value get tried first. But it will
// avoid trashing old values that worked. If you
// understand this comment, you probably didn't need
// to read it!!
pri->lLastParm = lCurParm;
if (pri->lpbiPrev)
{
GlobalFreePtr(pri->lpbiPrev);
pri->lpbiPrev = NULL;
}
if (lpbiRle)
{
if (pri->iStart)
{
lpbiRle->biCompression = BI_DIBX; // 1st part of DIB. Not
pri->lpbiPrev = CopyDib(lpbiTo, lpTo);// complete until next
} // BI_RLE8 is seen.
else
{
lpbiRle->biCompression = BI_RLE8;
}
}
return TRUE;
}
BOOL FAR PASCAL SplitDib(PRLEINST pri,
LPBITMAPINFOHEADER lpbiRle, LPBYTE pbRle,
LPBITMAPINFOHEADER lpbiPrev,LPBYTE pbPrev,
LPBITMAPINFOHEADER lpbiDib, LPBYTE pbDib)
{
int iStart, iLen, iMin, iMax;
DWORD dwSize;
BOOL f;
iStart = iMin = 0;
iLen = iMax = (int)lpbiDib->biHeight - iStart;
for(;;)
{
f = RleDeltaFrame(
lpbiRle, pbRle,
lpbiPrev,pbPrev,
lpbiDib, pbDib,
iStart,iLen,
pri->RleState.tolTemporal,
pri->RleState.tolSpatial,
pri->RleState.iMaxRunLen,4);
if (!f)
return FALSE;
dwSize = lpbiRle->biSizeImage;
DPF(("iStart=%d, iLen=%d, Size=%ld, Max=%ld", iStart, iLen, dwSize, pri->RleState.lMaxFrameSize));
if (dwSize < (DWORD)pri->RleState.lMaxFrameSize)
{
iMin = iLen;
if (iMax-iMin <= 1)
{
pri->iStart += iLen;
if (pri->iStart >= (int)lpbiDib->biHeight)
pri->iStart = 0;
return TRUE;
}
}
else
iMax = iLen - 1;
if (iStart != pri->iStart)
{
iStart = pri->iStart;
iLen = iMax = (int)lpbiDib->biHeight - iStart;
}
else
{
iLen = (iMin + iMax) / 2;
}
}
}