mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 10:22:40 +01:00
908 lines
21 KiB
C++
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
|
||
|
}
|
||
|
|