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