Microsoft-3D-Movie-Maker/kauai/SRC/CODEC.CPP

361 lines
8.2 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
Author: ShonK
Project: Kauai
Reviewed:
Copyright (c) Microsoft Corporation
Codec manager class.
***************************************************************************/
#include "util.h"
ASSERTNAME
RTCLASS(CODM)
RTCLASS(CODC)
/***************************************************************************
The header on a compressed block consists of the cfmt (a long in big
endian order) and the decompressed length (a long in big endian order).
***************************************************************************/
const long kcbCodecHeader = 2 * size(long);
/***************************************************************************
Constructor for the compression manager. pcodc is an optional default
codec. cfmt is the default compression format.
***************************************************************************/
CODM::CODM(PCODC pcodc, long cfmt)
{
AssertNilOrPo(pcodc, 0);
Assert(cfmtNil != cfmt, "nil default compression format");
_cfmtDef = cfmt;
_pcodcDef = pcodc;
if (pvNil != _pcodcDef)
_pcodcDef->AddRef();
_pglpcodc = pvNil;
AssertThis(0);
}
/***************************************************************************
Destructor for the compression manager.
***************************************************************************/
CODM::~CODM(void)
{
AssertThis(0);
ReleasePpo(&_pcodcDef);
if (pvNil != _pglpcodc)
{
long ipcodc;
PCODC pcodc;
for (ipcodc = _pglpcodc->IvMac(); ipcodc-- > 0; )
{
_pglpcodc->Get(ipcodc, &pcodc);
ReleasePpo(&pcodc);
}
ReleasePpo(&_pglpcodc);
}
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a CODM.
***************************************************************************/
void CODM::AssertValid(ulong grf)
{
CODM_PAR::AssertValid(0);
Assert(cfmtNil != _cfmtDef, "nil default compression");
AssertNilOrPo(_pcodcDef, 0);
AssertNilOrPo(_pglpcodc, 0);
}
/***************************************************************************
Mark memory for the CODM.
***************************************************************************/
void CODM::MarkMem(void)
{
AssertValid(0);
CODM_PAR::MarkMem();
MarkMemObj(_pcodcDef);
if (pvNil != _pglpcodc)
{
long ipcodc;
PCODC pcodc;
for (ipcodc = _pglpcodc->IvMac(); ipcodc-- > 0; )
{
_pglpcodc->Get(ipcodc, &pcodc);
MarkMemObj(pcodc);
}
MarkMemObj(_pglpcodc);
}
}
#endif //DEBUG
/***************************************************************************
Set the default compression type.
***************************************************************************/
void CODM::SetCfmtDefault(long cfmt)
{
AssertThis(0);
if (cfmt == cfmtNil)
{
Bug("can't set default compression to nil");
return;
}
_cfmtDef = cfmt;
}
/***************************************************************************
Add a codec to the compression manager.
***************************************************************************/
bool CODM::FRegisterCodec(PCODC pcodc)
{
AssertThis(0);
AssertPo(pcodc, 0);
if (pvNil == _pglpcodc && pvNil == (_pglpcodc = GL::PglNew(size(PCODC))))
return fFalse;
if (!_pglpcodc->FAdd(&pcodc))
return fFalse;
pcodc->AddRef();
return fTrue;
}
/***************************************************************************
Return whether we can encode or decode the given format.
***************************************************************************/
bool CODM::FCanDo(long cfmt, bool fEncode)
{
AssertThis(0);
PCODC pcodc;
if (cfmtNil == cfmt)
return fFalse;
return _FFindCodec(fEncode, cfmt, &pcodc);
}
/***************************************************************************
Gets the type of compression used on the block (assuming it is
compressed).
***************************************************************************/
bool CODM::FGetCfmtFromBlck(PBLCK pblck, long *pcfmt)
{
AssertThis(0);
AssertPo(pblck, 0);
AssertVarMem(pcfmt);
byte rgb[4];
TrashVar(pcfmt);
if (pblck->Cb(fTrue) < 2 * size(rgb))
return fFalse;
if (!pblck->FReadRgb(rgb, size(rgb), 0, fTrue))
return fFalse;
*pcfmt = LwFromBytes(rgb[0], rgb[1], rgb[2], rgb[3]);
return fTrue;
}
/***************************************************************************
Look for a codec that can handle the given format.
***************************************************************************/
bool CODM::_FFindCodec(bool fEncode, long cfmt, PCODC *ppcodc)
{
AssertThis(0);
AssertVarMem(ppcodc);
Assert(cfmtNil != cfmt, "nil cfmt");
if (pvNil != _pcodcDef && _pcodcDef->FCanDo(fEncode, cfmt))
{
*ppcodc = _pcodcDef;
return fTrue;
}
if (pvNil != _pglpcodc)
{
long ipcodc;
PCODC pcodc;
for (ipcodc = _pglpcodc->IvMac(); ipcodc-- > 0; )
{
_pglpcodc->Get(ipcodc, &pcodc);
if (pcodc->FCanDo(fEncode, cfmt))
{
*ppcodc = pcodc;
return fTrue;
}
}
}
return fFalse;
}
/***************************************************************************
Compress or decompress an hq of data. Note that the value of *phq
may change. cfmt should be cfmtNil to decompress.
***************************************************************************/
bool CODM::_FCodePhq(long cfmt, HQ *phq)
{
AssertThis(0);
AssertVarMem(phq);
AssertHq(*phq);
HQ hqDst;
void *pvSrc;
long cbSrc;
long cbDst;
bool fRet;
pvSrc = PvLockHq(*phq);
cbSrc = CbOfHq(*phq);
fRet = _FCode(cfmt, pvSrc, cbSrc, pvNil, 0, &cbDst);
UnlockHq(*phq);
if (!fRet)
return fFalse;
if (!FAllocHq(&hqDst, cbDst, fmemNil, mprNormal))
return fFalse;
pvSrc = PvLockHq(*phq);
fRet = _FCode(cfmt, pvSrc, cbSrc, PvLockHq(hqDst), cbDst, &cbDst);
UnlockHq(hqDst);
UnlockHq(*phq);
if (!fRet)
{
FreePhq(&hqDst);
return fFalse;
}
Assert(cbDst <= CbOfHq(hqDst), "why is the final size larger?");
if (cbDst < CbOfHq(hqDst))
AssertDo(FResizePhq(&hqDst, cbDst, fmemNil, mprNormal), 0);
FreePhq(phq);
*phq = hqDst;
return fTrue;
}
/***************************************************************************
Compress or decompress a block of data. If pvDst is nil, just fill
*pcbDst with the required destination buffer size. This is just an
estimate in the compress case.
***************************************************************************/
bool CODM::_FCode(long cfmt, void *pvSrc, long cbSrc,
void *pvDst, long cbDst, long *pcbDst)
{
AssertIn(cbSrc, 1, kcbMax);
AssertPvCb(pvSrc, cbSrc);
AssertIn(cbDst, 0, kcbMax);
AssertPvCb(pvDst, cbDst);
AssertVarMem(pcbDst);
byte *prgb;
PCODC pcodc;
if (cfmtNil != cfmt)
{
// Encode the data
if (pvNil == pvDst || cbDst >= cbSrc)
cbDst = cbSrc - 1;
if (cbDst <= kcbCodecHeader)
{
// destination is smaller than the minimum compressed size, so
// no sense trying.
return fFalse;
}
// make sure we have a codec for this format
if (!_FFindCodec(fTrue, cfmt, &pcodc))
return fFalse;
if (pvNil == pvDst)
{
// this is our best guess at the compressed size
*pcbDst = cbDst;
return fTrue;
}
prgb = (byte *)pvDst;
if (!pcodc->FConvert(fTrue, cfmt, pvSrc, cbSrc, prgb + kcbCodecHeader,
cbDst - kcbCodecHeader, pcbDst))
{
return fFalse;
}
AssertIn(*pcbDst, 1, cbDst - kcbCodecHeader + 1);
*pcbDst += kcbCodecHeader;
prgb[0] = B3Lw(cfmt);
prgb[1] = B2Lw(cfmt);
prgb[2] = B1Lw(cfmt);
prgb[3] = B0Lw(cfmt);
prgb[4] = B3Lw(cbSrc);
prgb[5] = B2Lw(cbSrc);
prgb[6] = B1Lw(cbSrc);
prgb[7] = B0Lw(cbSrc);
}
else
{
// decode
if (0 >= (cbSrc -= kcbCodecHeader))
return fFalse;
// get the format and decompressed size
prgb = (byte *)pvSrc;
cfmt = LwFromBytes(prgb[0], prgb[1], prgb[2], prgb[3]);
*pcbDst = LwFromBytes(prgb[4], prgb[5], prgb[6], prgb[7]);
if (!FIn(*pcbDst, 1, pvNil == pvDst ? kcbMax : cbDst + 1))
return fFalse;
// make sure we have a codec for this format
if (!_FFindCodec(fFalse, cfmt, &pcodc))
return fFalse;
if (pvNil == pvDst)
return fTrue;
cbDst = *pcbDst;
if (!pcodc->FConvert(fFalse, cfmt,
prgb + kcbCodecHeader, cbSrc, pvDst, cbDst, pcbDst))
{
return fFalse;
}
if (cbDst != *pcbDst)
{
Bug("decompressed to wrong size");
return fFalse;
}
}
return fTrue;
}