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

908 lines
21 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
Author: ShonK
Project: Kauai
Reviewed:
Copyright (c) Microsoft Corporation
Masked bitmap routines that a tool might use.
***************************************************************************/
#include "frame.h"
ASSERTNAME
RTCLASS(MBMP)
/***************************************************************************
Destructor for a masked bitmap.
***************************************************************************/
MBMP::~MBMP(void)
{
AssertBaseThis(0);
FreePhq(&_hqrgb);
}
/***************************************************************************
Static method to create a new MBMP based on the given prgbPixels with
extracting rectangle *prc and transparent color bTransparent. prgbPixels
should be a two-dimensional matrix of 8-bit pixels with width cbRow and
height dyp. prc should be the desired extracting rectangle. xp and yp
are the coordinates for the reference point of the MBMP (with (0,0) being
the upper-left corner). bTransparent should be the pixel value for the
transparent color for the MBMP.
***************************************************************************/
PMBMP MBMP::PmbmpNew(byte *prgbPixels, long cbRow, long dyp, RC *prc,
long xpRef, long ypRef, byte bTransparent, ulong grfmbmp, byte bDefault)
{
AssertIn(cbRow, 1, kcbMax);
AssertIn(dyp, 1, kcbMax);
AssertPvCb(prgbPixels, LwMul(cbRow, dyp));
AssertVarMem(prc);
PMBMP pmbmp;
if (pvNil != (pmbmp = NewObj MBMP) && !pmbmp->_FInit(prgbPixels, cbRow,
dyp, prc, xpRef, ypRef, bTransparent, grfmbmp, bDefault))
{
ReleasePpo(&pmbmp);
}
return pmbmp;
}
/***************************************************************************
Initialize the MBMP based on the given pixels.
***************************************************************************/
bool MBMP::_FInit(byte *prgbPixels, long cbRow, long dyp, RC *prc,
long xpRef, long ypRef, byte bTransparent, ulong grfmbmp, byte bDefault)
{
AssertIn(cbRow, 1, kcbMax);
AssertIn(dyp, 1, kcbMax);
AssertPvCb(prgbPixels, LwMul(cbRow, dyp));
AssertVarMem(prc);
Assert(!prc->FEmpty() && prc->xpLeft >= 0 && prc->xpRight <= cbRow &&
prc->ypTop >= 0 && prc->ypBottom <= dyp, "Invalid rectangle");
short *qrgcb;
byte *pb, *pbRow, *pbLimRow;
byte *qbDst, *qbDstPrev;
long cbPixelData, cbPrev, cbOpaque, cbRun;
long dibRow;
bool fTrans, fMask;
long xpMin, xpLim, ypLim, xp, yp;
RC rc = *prc;
// allocate enough space for the rgcb
if (!FAllocHq(&_hqrgb, size(MBMPH) + LwMul(rc.Dyp(), size(short)),
fmemNil, mprNormal))
{
return fFalse;
}
_Qmbmph()->fMask = fMask = FPure(grfmbmp & fmbmpMask);
_Qmbmph()->bFill = bDefault;
dibRow = (grfmbmp & fmbmpUpsideDown) ? -cbRow : cbRow;
// crop the bitmap and get an upper bound on the size of the pixel data
pbRow = prgbPixels + LwMul(cbRow,
((grfmbmp & fmbmpUpsideDown) ? dyp - rc.ypTop - 1 : rc.ypTop));
qrgcb = _Qrgcb();
xpMin = rc.xpRight;
xpLim = rc.xpLeft;
ypLim = rc.ypTop;
cbPixelData = 0;
for (yp = rc.ypTop; yp < rc.ypBottom; pbRow += dibRow, yp++)
{
pb = pbRow + rc.xpLeft;
pbLimRow = pbRow + rc.xpRight;
cbPrev = cbPixelData;
cbRun = cbOpaque = 0;
fTrans = fTrue;
for (;;)
{
// check for overflow or change in transparent status, or the
// end of the row
if (pb == pbLimRow || fTrans != (*pb == bTransparent) ||
cbRun == kbMax)
{
if (fTrans && pb == pbLimRow)
break;
cbPixelData++;
if (!fTrans && cbRun > 0)
{
xp = pb - pbRow;
if (xpMin > xp - cbRun)
xpMin = xp - cbRun;
if (xpLim < xp)
xpLim = xp;
cbOpaque += cbRun;
if (!fMask)
cbPixelData += cbRun;
}
if (pb == pbLimRow)
break;
cbRun = 0;
fTrans = !fTrans;
}
else
{
// Increment pixel count and go on to next pixel.
cbRun++;
pb++;
}
}
if (0 == cbOpaque)
{
// nothing in this row but transparent pixels
if (yp == rc.ypTop)
rc.ypTop++;
else
qrgcb[yp - rc.ypTop] = 0;
cbPixelData = cbPrev;
}
else
{
// set the row length in the rgcb.
AssertIn(cbPixelData - cbPrev, 2 + !fMask, kswMax + 1);
qrgcb[yp - rc.ypTop] = (short)(cbPixelData - cbPrev);
ypLim = yp + 1;
}
}
rc.ypBottom = ypLim;
rc.xpLeft = xpMin;
rc.xpRight = xpLim;
if (rc.FEmpty())
{
Warn("Empty source bitmap for MBMP");
rc.Zero();
}
//reallocate the _hqrgb to the size actually needed
AssertIn(LwMul(rc.Dyp(), size(short)), 0,
CbOfHq(_hqrgb) - size(MBMPH) + 1);
_cbRgcb = LwMul(rc.Dyp(), size(short));
if (!FResizePhq(&_hqrgb, _cbRgcb + size(MBMPH) + cbPixelData,
fmemNil, mprNormal))
{
return fFalse;
}
//now actually construct the pixel data
qrgcb = _Qrgcb();
qbDst = (byte *)PvAddBv(qrgcb, _cbRgcb);
pbRow = prgbPixels + LwMul(cbRow,
((grfmbmp & fmbmpUpsideDown) ? dyp - rc.ypTop - 1 : rc.ypTop));
for (yp = rc.ypTop; yp < rc.ypBottom; pbRow += dibRow, yp++)
{
if (qrgcb[yp - rc.ypTop] == 0)
{
//empty row, no need to scan it
AssertIn(yp, rc.ypTop + 1, rc.ypBottom);
continue;
}
pb = pbRow + rc.xpLeft;
pbLimRow = pbRow + rc.xpRight;
qbDstPrev = qbDst;
cbRun = 0;
fTrans = fTrue;
for (;;)
{
// check for overflow or change in transparent status, or the
// end of the row
if (pb == pbLimRow || fTrans != (*pb == bTransparent) ||
cbRun == kbMax)
{
if (fTrans && pb == pbLimRow)
break;
*qbDst++ = (byte)cbRun;
if (!fTrans)
{
if (!fMask)
{
CopyPb(pb - cbRun, qbDst, cbRun);
qbDst += cbRun;
}
if (pb == pbLimRow)
break;
}
cbRun = 0;
fTrans = !fTrans;
}
else
{
// Increment pixel count and go on to next pixel.
cbRun++;
pb++;
}
}
// Set the row length in the rgcb.
cbRun = qbDst - qbDstPrev;
AssertIn(cbRun, 2 + !fMask, qrgcb[yp - rc.ypTop] + 1);
qrgcb[yp - rc.ypTop] = (short)cbRun;
}
//shrink _hqrgb to the actual size needed
cbRun = BvSubPvs(qbDst, QvFromHq(_hqrgb));
AssertIn(cbRun, 0, CbOfHq(_hqrgb) + 1);
AssertDo(FResizePhq(&_hqrgb, cbRun, fmemNil, mprNormal),
"shrinking failed!");
//set the bounding rectangle of the MBMP
rc.Offset(-xpRef, -ypRef);
_Qmbmph()->rc = rc;
AssertThis(0);
return fTrue;
}
/****************************************************************************
This function will read in a bitmap file and return a PMBMP made from it.
(xp, yp) will be the reference point of the mbmp [(0,0) is uppper-left].
All pixels with the same value as bTransparent will be read in as
transparent. The bitmap file must be uncompressed and have a bit depth
of 8. The palette information is be ignored.
****************************************************************************/
PMBMP MBMP::PmbmpReadNative(FNI *pfni, byte bTransparent, long xp, long yp,
ulong grfmbmp, byte bDefault)
{
AssertPo(pfni, ffniFile);
byte *prgb;
RC rc;
long dxp, dyp;
bool fUpsideDown;
PMBMP pmbmp = pvNil;
if (!FReadBitmap(pfni, &prgb, pvNil, &dxp, &dyp, &fUpsideDown, bTransparent))
return pvNil;
rc.Set(0, 0, dxp, dyp);
pmbmp = MBMP::PmbmpNew(prgb, CbRoundToLong(rc.xpRight),
rc.ypBottom, &rc, xp, yp, bTransparent,
(fUpsideDown ? grfmbmp : grfmbmp ^ fmbmpUpsideDown), bDefault);
FreePpv((void **)&prgb);
return pmbmp;
}
/***************************************************************************
Read a masked bitmap from a block. May free the block or modify it.
***************************************************************************/
PMBMP MBMP::PmbmpRead(PBLCK pblck)
{
AssertPo(pblck, 0);
PMBMP pmbmp;
MBMPH *qmbmph;
long cbRgcb;
long cbTot;
bool fSwap;
RC rc;
HQ hqrgb = hqNil;
if (!pblck->FUnpackData())
return pvNil;
cbTot = pblck->Cb();
if (cbTot < size(MBMPH) || hqNil == (hqrgb = pblck->HqFree()))
return pvNil;
qmbmph = (MBMPH *)QvFromHq(hqrgb);
fSwap = (kboOther == qmbmph->bo);
if (fSwap)
SwapBytesBom(qmbmph, kbomMbmph);
else if (qmbmph->bo != kboCur)
goto LFail;
if (qmbmph->swReserved != 0 || qmbmph->cb != cbTot)
goto LFail;
rc = qmbmph->rc;
if (rc.FEmpty())
{
if (cbTot != size(MBMPH))
goto LFail;
qmbmph->rc.xpRight = rc.xpLeft;
qmbmph->rc.ypBottom = rc.ypTop;
}
cbRgcb = LwMul(rc.Dyp(), size(short));
if (size(MBMPH) + cbRgcb > cbTot)
goto LFail;
if (pvNil == (pmbmp = NewObj MBMP))
{
LFail:
FreePhq(&hqrgb);
return pvNil;
}
pmbmp->_cbRgcb = cbRgcb;
pmbmp->_hqrgb = hqrgb;
if (fSwap)
{
//swap bytes in the rgcb
SwapBytesRgsw(pmbmp->_Qrgcb(), rc.Dyp());
}
#ifdef DEBUG
//verify the rgcb and rgb
long ccb, cb, dxp;
byte *qb;
bool fMask = pmbmp->_Qmbmph()->fMask;
short *qcb = pmbmp->_Qrgcb();
byte *qbRow = (byte *)PvAddBv(qcb, cbRgcb);
cbTot -= size(MBMPH) + cbRgcb;
for (ccb = rc.Dyp(); ccb-- > 0; )
{
cb = *qcb++;
if (!FIn(cb, 0, cbTot + 1))
goto LFailDebug;
cbTot -= cb;
qb = qbRow;
dxp = 0;
while (qb < qbRow + cb)
{
dxp += *qb++;
if (qb >= qbRow + cb)
break;
dxp += *qb;
if (fMask)
qb++;
else
qb += *qb + 1;
}
if (dxp > rc.Dxp() || qb != qbRow + cb)
goto LFailDebug;
qbRow = qb;
}
if (cbTot != 0)
{
LFailDebug:
Bug("Attempted to read bad MBMP");
ReleasePpo(&pmbmp);
}
#endif //DEBUG
AssertNilOrPo(pmbmp, 0);
return pmbmp;
}
/***************************************************************************
Return the total size on file.
***************************************************************************/
long MBMP::CbOnFile(void)
{
AssertThis(0);
return CbOfHq(_hqrgb);
}
/***************************************************************************
Write the masked bitmap (and its header) to the given block.
***************************************************************************/
bool MBMP::FWrite(PBLCK pblck)
{
AssertThis(0);
AssertPo(pblck, 0);
MBMPH *qmbmph;
qmbmph = _Qmbmph();
qmbmph->bo = kboCur;
qmbmph->osk = koskCur;
qmbmph->swReserved = 0;
qmbmph->cb = CbOfHq(_hqrgb);
if (qmbmph->cb != pblck->Cb())
{
Bug("Wrong sized block");
return fFalse;
}
if (!pblck->FWriteHq(_hqrgb, 0))
return fFalse;
return fTrue;
}
/***************************************************************************
Get the natural rectangle for the mbmp.
***************************************************************************/
void MBMP::GetRc(RC *prc)
{
AssertThis(0);
*prc = _Qmbmph()->rc;
}
/***************************************************************************
Return whether the given (xp, yp) is in a non-transparent pixel of
the MBMP. (xp, yp) should be given in MBMP coordinates.
***************************************************************************/
bool MBMP::FPtIn(long xp, long yp)
{
AssertThis(0);
byte *qb, *qbLim;
short *qcb;
short cb;
MBMPH *qmbmph;
qmbmph = _Qmbmph();
if (!qmbmph->rc.FPtIn(xp, yp))
return fFalse;
qcb = _Qrgcb();
qb = (byte *)PvAddBv(qcb, _cbRgcb);
for (yp -= qmbmph->rc.ypTop; yp-- > 0; )
qb += *qcb++;
qbLim = qb + *qcb;
for (xp -= qmbmph->rc.xpLeft; qb < qbLim; )
{
if (0 > (xp -= *qb++) || qb >= qbLim)
break;
cb = *qb++;
if (0 > (xp -= cb))
return fTrue;
if (!qmbmph->fMask)
qb += cb;
}
Assert(qb <= qbLim, "bad row in MBMP");
return fFalse;
}
#ifdef DEBUG
/***************************************************************************
Assert the validity of a MBMP.
***************************************************************************/
void MBMP::AssertValid(ulong grf)
{
long ccb;
long cbTot;
short *qcb;
RC rc;
MBMP_PAR::AssertValid(0);
AssertHq(_hqrgb);
rc = _Qmbmph()->rc;
ccb = rc.Dyp();
Assert(_cbRgcb == LwMul(rc.Dyp(), size(short)),
"_cbRgcb wrong");
cbTot = 0;
qcb = _Qrgcb();
while (ccb-- > 0)
{
AssertIn(*qcb, 0, kcbMax);
cbTot += *qcb++;
}
Assert(cbTot + _cbRgcb + size(MBMPH) == CbOfHq(_hqrgb),
"_hqrgb wrong size");
}
/***************************************************************************
Mark memory for the MBMP.
***************************************************************************/
void MBMP::MarkMem(void)
{
AssertValid(0);
MBMP_PAR::MarkMem();
MarkHq(_hqrgb);
}
#endif //DEBUG
/***************************************************************************
A PFNRPO to read an MBMP.
***************************************************************************/
bool MBMP::FReadMbmp(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
PBACO *ppbaco, long *pcb)
{
AssertPo(pcrf, 0);
AssertPo(pblck, fblckReadable);
AssertNilOrVarMem(ppbaco);
AssertVarMem(pcb);
PMBMP pmbmp;
*pcb = pblck->Cb(fTrue);
if (pvNil == ppbaco)
return fTrue;
if (!pblck->FUnpackData())
goto LFail;
*pcb = pblck->Cb();
if (pvNil == (pmbmp = PmbmpRead(pblck)))
{
LFail:
TrashVar(ppbaco);
TrashVar(pcb);
return fFalse;
}
*ppbaco = pmbmp;
return fTrue;
}
/***************************************************************************
Given an FNI refering to a bitmap file, returns the interesting parts of
the header and the pixel data and palette. Fails if the bitmap is not
8 bits, uncompressed. Any or all of the output pointers may be nil.
input:
pfni -- the file from which to read the data
output:
The following variable parameters are valid only if FReadBitmap
returns true (*pprgb and *ppglclr will be nil if this fails):
pprgb -- pointer to the allocated memory for pixel data
ppglclr -- the palette
pdxp -- the width of the bitmap
pdyp -- the height of the bitmap
pfUpsideDown -- fTrue if the bitmap is upside down
returns fTrue if it succeeds
***************************************************************************/
bool FReadBitmap(FNI *pfni, byte **pprgb, PGL *ppglclr, long *pdxp, long *pdyp,
bool *pfUpsideDown, byte bTransparent)
{
AssertPo(pfni, ffniFile);
AssertNilOrVarMem(pprgb);
AssertNilOrVarMem(ppglclr);
AssertNilOrVarMem(pdxp);
AssertNilOrVarMem(pdyp);
AssertNilOrVarMem(pfUpsideDown);
#ifdef WIN
#pragma pack(2) //the stupid bmfh is an odd number of shorts
struct BMH
{
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
};
#pragma pack()
PFIL pfil;
RC rc;
long fpMac, cbBitmap, cbSrc;
BMH bmh;
bool fRet, fRle;
FP fpCur = 0;
if (pvNil != pprgb)
*pprgb = pvNil;
if (pvNil != ppglclr)
*ppglclr = pvNil;
if (pvNil == (pfil = FIL::PfilOpen(pfni)))
return fFalse;
fpMac = pfil->FpMac();
if (size(BMH) >= fpMac || !pfil->FReadRgbSeq(&bmh, size(BMH), &fpCur))
goto LFail;
fRle = (bmh.bmih.biCompression == BI_RLE8);
cbSrc = bmh.bmih.biSizeImage;
if (((long)bmh.bmfh.bfSize != fpMac) ||
bmh.bmfh.bfType != 'MB' || !FIn(bmh.bmfh.bfOffBits, size(BMH), fpMac) ||
bmh.bmfh.bfReserved1 != 0 || bmh.bmfh.bfReserved2 != 0 ||
bmh.bmih.biSize != size(bmh.bmih) || bmh.bmih.biPlanes != 1)
{
Warn("bad bitmap file");
goto LFail;
}
if (bmh.bmih.biBitCount != 8)
{
Warn("not an 8-bit bitmap");
goto LFail;
}
if (bmh.bmih.biCompression != BI_RGB && !fRle ||
cbSrc != fpMac - (long)bmh.bmfh.bfOffBits && (cbSrc != 0 || fRle))
{
Warn("bad compression type or bitmap file is wrong length");
goto LFail;
}
rc.Set(0, 0, bmh.bmih.biWidth, LwAbs(bmh.bmih.biHeight));
cbBitmap = CbRoundToLong(rc.xpRight) * rc.ypBottom;
if (rc.FEmpty())
{
Warn("Empty bitmap rectangle");
goto LFail;
}
if (!fRle)
{
if (cbSrc == 0)
cbSrc = cbBitmap;
else if (cbSrc != cbBitmap)
{
Warn("Bitmap data wrong size");
goto LFail;
}
}
if (pvNil != ppglclr)
{
// get the palette
if (bmh.bmih.biClrUsed != 0 && bmh.bmih.biClrUsed != 256)
{
Warn("palette is wrong size");
goto LFail;
}
if (pvNil == (*ppglclr = GL::PglNew(size(CLR), 256)))
goto LFail;
AssertDo((*ppglclr)->FSetIvMac(256), 0);
fRet = pfil->FReadRgbSeq((*ppglclr)->PvLock(0),
LwMul(size(CLR), 256), &fpCur);
(*ppglclr)->Unlock();
if (!fRet)
goto LFail;
}
if (pvNil == pprgb)
goto LDone;
if (fRle)
{
byte *pbSrc, *pbDst, *prgbSrc, *pbLimSrc, *pbLimDst;
byte bT;
long xp, cbT;
long cbRowDst = CbRoundToLong(rc.xpRight);
// get the source
if (!FAllocPv((void **)&prgbSrc, cbSrc, fmemNil, mprNormal))
goto LFail;
if (!pfil->FReadRgb(prgbSrc, cbSrc, bmh.bmfh.bfOffBits) ||
!FAllocPv((void **)pprgb, cbBitmap, fmemNil, mprNormal))
{
goto LBad;
}
pbDst = *pprgb;
pbSrc = prgbSrc;
pbLimSrc = pbSrc + cbSrc;
pbLimDst = pbDst + cbBitmap;
xp = 0;
for (;;)
{
if (2 > pbLimSrc - pbSrc)
goto LBad;
bT = *pbSrc++;
if (bT != 0)
{
if (bT > pbLimDst - pbDst || bT > cbRowDst - xp)
goto LBad;
FillPb(pbDst, bT, *pbSrc++);
pbDst += bT;
xp += bT;
continue;
}
// escaped
bT = *pbSrc++;
switch (bT)
{
case 0:
// end of line
if (cbRowDst > xp)
FillPb(pbDst, cbRowDst - xp, bTransparent);
pbDst += cbRowDst - xp;
xp = 0;
break;
case 1:
// end of bitmap
if (pbLimDst > pbDst)
FillPb(pbDst, pbLimDst - pbDst, bTransparent);
goto LRleDone;
case 2:
// delta
if (2 > pbLimSrc - pbSrc)
goto LBad;
cbT = pbSrc[0] + cbRowDst * pbSrc[1];
if (cbT > pbLimDst - pbDst || pbSrc[0] > cbRowDst - xp)
goto LBad;
FillPb(pbDst, cbT, bTransparent);
pbDst += cbT;
xp += pbSrc[0];
pbSrc += 2;
break;
default:
// literal run
cbT = LwRoundAway(bT, 2);
if (cbT > pbLimSrc - pbSrc || bT > pbLimDst - pbDst ||
bT > cbRowDst - xp)
{
goto LBad;
}
CopyPb(pbSrc, pbDst, bT);
pbDst += bT;
pbSrc += cbT;
xp += bT;
break;
}
}
LBad:
// rle encoding is bad
FreePpv((void **)&prgbSrc);
Warn("compressed bitmap is bad");
goto LFail;
LRleDone:
FreePpv((void **)&prgbSrc);
}
else
{
// non-rle: get the bits
if (!FAllocPv((void **)pprgb, cbBitmap, fmemNil, mprNormal))
goto LFail;
if (!pfil->FReadRgb(*pprgb, cbBitmap, bmh.bmfh.bfOffBits))
{
FreePpv((void **)pprgb);
LFail:
PushErc(ercMbmpCantOpenBitmap);
if (pvNil != pprgb)
*pprgb = pvNil;
if (pvNil != ppglclr)
ReleasePpo(ppglclr);
TrashVar(pdxp);
TrashVar(pdyp);
TrashVar(pfUpsideDown);
ReleasePpo(&pfil);
return fFalse;
}
}
LDone:
if (pvNil != pdxp)
*pdxp = bmh.bmih.biWidth;
if (pvNil != pdyp)
*pdyp = LwAbs(bmh.bmih.biHeight);
if (pvNil != pfUpsideDown)
*pfUpsideDown = bmh.bmih.biHeight < 0;
ReleasePpo(&pfil);
return fTrue;
#endif //WIN
#ifdef MAC
if (pvNil != pprgb)
*pprgb = pvNil;
if (pvNil != ppglclr)
*ppglclr = pvNil;
TrashVar(pdxp);
TrashVar(pdyp);
TrashVar(pfUpsideDown);
RawRtn(); // REVIEW peted: Mac FReadBitmap NYI
return fFalse;
#endif //MAC
}
/***************************************************************************
Writes a given bitmap to a given file.
Arguments:
FNI *pfni -- the name of the file to write
byte *prgb -- the bits in the bitmap
PGL pglclr -- the palette of the bitmap
long dxp -- the width of the bitmap
long dyp -- the height of the bitmap
bool fUpsideDown -- indicates if the rows should be inverted
Returns: fTrue if it could write the file
***************************************************************************/
bool FWriteBitmap(FNI *pfni, byte *prgb, PGL pglclr, long dxp, long dyp,
bool fUpsideDown)
{
AssertPo(pfni, ffniFile);
AssertVarMem(prgb);
AssertPo(pglclr, 0);
Assert(pglclr->IvMac() == 256, "Invalid color palette");
Assert(dxp >= 0, "Invalid width");
Assert(dyp >= 0, "Invalid height");
#ifdef WIN
Assert(pglclr->CbEntry() == size(RGBQUAD),
"Palette has different format from Windows");
#pragma pack(2) //the stupid bmfh is an odd number of shorts
struct BMH
{
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
};
#pragma pack()
bool fRet = fFalse;
long cbSrc;
PFIL pfil = pvNil;
FP fpCur = 0;
BMH bmh;
cbSrc = CbRoundToLong(dxp) * dyp;
/* Fill in the header */
bmh.bmfh.bfType = 'MB';
bmh.bmfh.bfSize = size(bmh) + LwMul(size(RGBQUAD), 256) + cbSrc;
bmh.bmfh.bfOffBits = size(bmh) + LwMul(size(RGBQUAD), 256);
bmh.bmfh.bfReserved1 = bmh.bmfh.bfReserved2 = 0;
bmh.bmih.biSize = size(bmh.bmih);
bmh.bmih.biWidth = dxp;
bmh.bmih.biHeight = dyp;
bmh.bmih.biPlanes = 1;
bmh.bmih.biBitCount = 8;
bmh.bmih.biCompression = BI_RGB;
bmh.bmih.biSizeImage = 0;
bmh.bmih.biXPelsPerMeter = 0;
bmh.bmih.biYPelsPerMeter = 0;
bmh.bmih.biClrUsed = 256;
bmh.bmih.biClrImportant = 256;
/* Write the header */
if (pvNil == (pfil = FIL::PfilCreate(pfni)))
goto LFail;
if (!pfil->FWriteRgbSeq(&bmh, size(BMH), &fpCur))
goto LFail;
/* Write the palette */
if (!pfil->FWriteRgbSeq(pglclr->PvLock(0), LwMul(size(CLR), 256), &fpCur))
{
pglclr->Unlock();
goto LFail;
}
pglclr->Unlock();
/* Write the bits */
Assert((ulong)fpCur == bmh.bmfh.bfOffBits,
"Current file pos is wrong");
if (fUpsideDown)
{
byte *pbCur;
long cbRow = CbRoundToLong(dxp);
pbCur = prgb + dyp * cbRow;
fRet = fTrue;
while (pbCur > prgb && fRet)
{
pbCur -= cbRow;
fRet = pfil->FWriteRgbSeq(pbCur, cbRow, &fpCur);
}
}
else
fRet = pfil->FWriteRgbSeq(prgb, cbSrc, &fpCur);
LFail:
if (pfil != pvNil)
{
if (!fRet)
pfil->SetTemp();
ReleasePpo(&pfil);
}
return fRet;
#endif //WIN
#ifdef MAC
RawRtn(); // REVIEW peted: Mac FWriteBitmap NYI
return fFalse;
#endif //MAC
}