mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-25 11:42:35 +01:00
338 lines
8.7 KiB
C++
338 lines
8.7 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
|
|
tdf.cpp: Three-D Font class
|
|
|
|
Primary Author: ******
|
|
Review Status: REVIEWED - any changes to this file must be reviewed!
|
|
|
|
|
|
TDFs (3-D Fonts) are simply collections of models, one model per ASCII
|
|
character. The TDF class holds general font information such as the
|
|
count of characters in the font and the maximum height of the
|
|
characters. It also holds an array of widths and heights of every
|
|
character, to allow proportional spacing. Fetching a letter's model
|
|
from a TDF involves looking for a child chunk of the TDF chunk with a
|
|
CHID equal to the ASCII value of the desired character:
|
|
|
|
TDF // Contains font info (width and height of characters)
|
|
|
|
|
+---BMDL (chid 0) // MODL for ASCII character 0
|
|
|
|
|
+---BMDL (chid 1) // MODL for ASCII character 1
|
|
.
|
|
.
|
|
.
|
|
|
|
***************************************************************************/
|
|
#include "soc.h"
|
|
ASSERTNAME
|
|
|
|
RTCLASS(TDF)
|
|
|
|
const long kcchTdfDefault = 256; // for size estimates and authoring
|
|
const BRS kdxrSpacing = BR_SCALAR(0.0); // horizontal space between chars
|
|
const BRS kdyrLeading = BR_SCALAR(0.5); // vertical space between chars
|
|
|
|
|
|
/****************************************
|
|
3-D Font On File
|
|
****************************************/
|
|
struct TDFF
|
|
{
|
|
short bo;
|
|
short osk;
|
|
long cch;
|
|
BRS dyrMax;
|
|
// These variable-length arrays follow the TDFF in the TDF chunk
|
|
// BRS rgdxr[cch];
|
|
// BRS rgdyr[cch];
|
|
};
|
|
const BOM kbomTdff = 0x5F000000; // don't forget to swap rgdxr & rgdyr!
|
|
|
|
|
|
/***************************************************************************
|
|
A PFNRPO to read a TDF from a file.
|
|
***************************************************************************/
|
|
bool TDF::FReadTdf(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck, PBACO *ppbaco,
|
|
long *pcb)
|
|
{
|
|
AssertPo(pcrf, 0);
|
|
AssertPo(pblck, 0);
|
|
AssertNilOrVarMem(ppbaco);
|
|
AssertVarMem(pcb);
|
|
|
|
TDF *ptdf;
|
|
|
|
// Estimate TDF size in memory.
|
|
if (pblck->FPacked())
|
|
*pcb = size(TDF) + LwMul(kcchTdfDefault, size(BRS) + size(BRS));
|
|
else
|
|
*pcb = pblck->Cb();
|
|
if (pvNil == ppbaco)
|
|
return fTrue;
|
|
ptdf = NewObj TDF;
|
|
if (pvNil == ptdf || !ptdf->_FInit(pblck))
|
|
{
|
|
TrashVar(ppbaco);
|
|
TrashVar(pcb);
|
|
ReleasePpo(&ptdf);
|
|
return fFalse;
|
|
}
|
|
AssertPo(ptdf, 0);
|
|
*pcb = size(TDF) + LwMul(ptdf->_cch, size(BRS) + size(BRS));
|
|
*ppbaco = ptdf;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the font. Does not clean up on failure because the
|
|
destructor will.
|
|
***************************************************************************/
|
|
bool TDF::_FInit(PBLCK pblck)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(pblck, 0);
|
|
|
|
TDFF tdff;
|
|
long cbrgdwr; // space taken by rgdxr or rgdyr
|
|
|
|
if (!pblck->FUnpackData())
|
|
return fFalse;
|
|
if (pblck->Cb() < size(TDFF))
|
|
{
|
|
PushErc(ercSocBadTdf);
|
|
return fFalse;
|
|
}
|
|
if (!pblck->FReadRgb(&tdff, size(TDFF), 0))
|
|
return fFalse;
|
|
if (kboCur != tdff.bo)
|
|
SwapBytesBom(&tdff, kbomTdff);
|
|
Assert(kboCur == tdff.bo, "bad TDFF");
|
|
_cch = tdff.cch;
|
|
cbrgdwr = LwMul(_cch, size(BRS));
|
|
if (pblck->Cb() != size(TDFF) + cbrgdwr + cbrgdwr)
|
|
{
|
|
PushErc(ercSocBadTdf);
|
|
return fFalse;
|
|
}
|
|
_dyrMax = tdff.dyrMax;
|
|
|
|
// Read _prgdxr
|
|
if (!FAllocPv((void **)&_prgdxr, cbrgdwr, fmemNil, mprNormal))
|
|
return fFalse;
|
|
if (!pblck->FReadRgb(_prgdxr, cbrgdwr, size(TDFF)))
|
|
return fFalse;
|
|
AssertBomRglw(kbomBrs, size(BRS));
|
|
if (kboCur != tdff.bo)
|
|
SwapBytesRglw(_prgdxr, _cch);
|
|
|
|
// Read _prgdyr
|
|
if (!FAllocPv((void **)&_prgdyr, cbrgdwr, fmemNil, mprNormal))
|
|
return fFalse;
|
|
if (!pblck->FReadRgb(_prgdyr, cbrgdwr, size(TDFF) + cbrgdwr))
|
|
return fFalse;
|
|
AssertBomRglw(kbomBrs, size(BRS));
|
|
if (kboCur != tdff.bo)
|
|
SwapBytesRglw(_prgdyr, _cch);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
TDF destructor
|
|
***************************************************************************/
|
|
TDF::~TDF(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
FreePpv((void **)&_prgdxr);
|
|
FreePpv((void **)&_prgdyr);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
This authoring-only API creates a new TDF chunk in pcrf, with child
|
|
models as specified in pglkid. This function does not create a new
|
|
TDF instance in memory...to do that, call FReadTdf with the values
|
|
returned in pckiTdf.
|
|
***************************************************************************/
|
|
bool TDF::FCreate(PCRF pcrf, PGL pglkid, STN *pstn, CKI *pckiTdf)
|
|
{
|
|
AssertPo(pcrf, 0);
|
|
AssertPo(pglkid, 0);
|
|
AssertPo(pstn, 0);
|
|
AssertNilOrVarMem(pckiTdf);
|
|
|
|
CKI ckiTdf;
|
|
KID kid;
|
|
KID kid2;
|
|
TDFF tdff;
|
|
BRS *prgdxr = pvNil;
|
|
BRS *prgdyr = pvNil;
|
|
PMODL pmodl;
|
|
BLCK blck;
|
|
long cbrgdwr; // space taken by rgdxr or rgdyr
|
|
long ikid;
|
|
long ckid;
|
|
CHID chidMax = 0;
|
|
long ikidLetteri = -1;
|
|
|
|
// Find chidMax
|
|
ckid = pglkid->IvMac();
|
|
for (ikid = 0; ikid < ckid; ikid++)
|
|
{
|
|
pglkid->Get(ikid, &kid);
|
|
if (kid.chid > chidMax)
|
|
chidMax = kid.chid;
|
|
if (kid.chid == (CHID)ChLit('i'))
|
|
ikidLetteri = ikid;
|
|
}
|
|
|
|
tdff.bo = kboCur;
|
|
tdff.osk = koskCur;
|
|
tdff.cch = chidMax + 1;
|
|
tdff.dyrMax = rZero;
|
|
cbrgdwr = LwMul(tdff.cch, size(BRS));
|
|
if (!FAllocPv((void **)&prgdxr, cbrgdwr, fmemClear, mprNormal))
|
|
goto LFail;
|
|
if (!FAllocPv((void **)&prgdyr, cbrgdwr, fmemClear, mprNormal))
|
|
goto LFail;
|
|
|
|
// Create the TDF chunk
|
|
ckiTdf.ctg = kctgTdf;
|
|
if (!pcrf->Pcfl()->FAdd(size(TDFF) + cbrgdwr + cbrgdwr, ckiTdf.ctg,
|
|
&ckiTdf.cno, &blck))
|
|
{
|
|
goto LFail;
|
|
}
|
|
|
|
// Add the BMDL kids and remember widths, heights, and maximum height
|
|
for (ikid = 0; ikid < ckid; ikid++)
|
|
{
|
|
pglkid->Get(ikid, &kid);
|
|
pmodl = (PMODL)pcrf->PbacoFetch(kid.cki.ctg, kid.cki.cno,
|
|
MODL::FReadModl);
|
|
if (pmodl == pvNil)
|
|
goto LFail;
|
|
if (!pcrf->Pcfl()->FAdoptChild(ckiTdf.ctg, ckiTdf.cno, kid.cki.ctg,
|
|
kid.cki.cno, kid.chid))
|
|
{
|
|
goto LFail;
|
|
}
|
|
if (pmodl->Dxr() == 0 && kid.chid == (CHID)ChLit(' ') &&
|
|
ikidLetteri != -1)
|
|
{
|
|
// Hack to turn null models into space characters:
|
|
// space is the width and height of an "i"
|
|
ReleasePpo(&pmodl);
|
|
pglkid->Get(ikidLetteri, &kid2);
|
|
pmodl = (PMODL)pcrf->PbacoFetch(kid2.cki.ctg, kid2.cki.cno,
|
|
MODL::FReadModl);
|
|
if (pvNil == pmodl)
|
|
goto LFail;
|
|
}
|
|
prgdxr[kid.chid] = pmodl->Dxr() + kdxrSpacing;
|
|
prgdyr[kid.chid] = pmodl->Dyr() + kdyrLeading;
|
|
if (prgdyr[kid.chid] > tdff.dyrMax)
|
|
tdff.dyrMax = prgdyr[kid.chid];
|
|
ReleasePpo(&pmodl);
|
|
|
|
}
|
|
if (!blck.FWriteRgb(&tdff, size(TDFF), 0))
|
|
goto LFail;
|
|
if (!blck.FWriteRgb(prgdxr, cbrgdwr, size(TDFF)))
|
|
goto LFail;
|
|
if (!blck.FWriteRgb(prgdyr, cbrgdwr, size(TDFF) + cbrgdwr))
|
|
goto LFail;
|
|
FreePpv((void **)&prgdxr);
|
|
FreePpv((void **)&prgdyr);
|
|
if (pvNil != pckiTdf)
|
|
*pckiTdf = ckiTdf;
|
|
return fTrue;
|
|
LFail:
|
|
FreePpv((void **)&prgdxr);
|
|
FreePpv((void **)&prgdyr);
|
|
TrashVar(pckiTdf);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a model for a character from the font. The chid is equal to the
|
|
ASCII (or Unicode) value of the desired character.
|
|
***************************************************************************/
|
|
PMODL TDF::PmodlFetch(CHID chid)
|
|
{
|
|
AssertThis(0);
|
|
|
|
KID kid;
|
|
|
|
if (!Pcrf()->Pcfl()->FGetKidChid(Ctg(), Cno(), chid, &kid))
|
|
{
|
|
STN stn;
|
|
stn.FFormatSz(PszLit("Couldn't find BMDL for 3-D Font with chid %d."),
|
|
chid);
|
|
Warn(stn.Psz());
|
|
PushErc(ercSocNoModlForChar);
|
|
return pvNil;
|
|
}
|
|
return (PMODL)Pcrf()->PbacoFetch(kid.cki.ctg, kid.cki.cno,
|
|
MODL::FReadModl);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the width of the given character
|
|
***************************************************************************/
|
|
BRS TDF::DxrChar(long ich)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ich, 0, _cch);
|
|
|
|
return _prgdxr[ich];
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the height of the given character
|
|
***************************************************************************/
|
|
BRS TDF::DyrChar(long ich)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ich, 0, _cch);
|
|
|
|
return _prgdyr[ich];
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the TDF.
|
|
***************************************************************************/
|
|
void TDF::AssertValid(ulong grf)
|
|
{
|
|
TDF_PAR::AssertValid(fobjAllocated);
|
|
AssertIn(_cch, 0, klwMax);
|
|
AssertIn(_dyrMax, 0, BR_SCALAR_MAX);
|
|
AssertPvCb(_prgdxr, LwMul(_cch, size(BRS)));
|
|
AssertPvCb(_prgdyr, LwMul(_cch, size(BRS)));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the TDF
|
|
***************************************************************************/
|
|
void TDF::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
TDF_PAR::MarkMem();
|
|
MarkPv(_prgdxr);
|
|
MarkPv(_prgdyr);
|
|
}
|
|
#endif //DEBUG
|