1358 lines
29 KiB
C++
1358 lines
29 KiB
C++
|
/* Copyright (c) Microsoft Corporation.
|
||
|
Licensed under the MIT License. */
|
||
|
|
||
|
/***************************************************************************
|
||
|
Author: ShonK
|
||
|
Project: Kauai
|
||
|
Reviewed:
|
||
|
Copyright (c) Microsoft Corporation
|
||
|
|
||
|
Region code.
|
||
|
|
||
|
***************************************************************************/
|
||
|
#include "frame.h"
|
||
|
ASSERTNAME
|
||
|
|
||
|
|
||
|
RTCLASS(REGN)
|
||
|
RTCLASS(REGSC)
|
||
|
|
||
|
long const kcdxpBlock = 100;
|
||
|
|
||
|
/***************************************************************************
|
||
|
Region builder class. For building the _pglxp of a region and getting
|
||
|
its bounding rectangle and _dxp value.
|
||
|
***************************************************************************/
|
||
|
typedef class REGBL *PREGBL;
|
||
|
#define REGBL_PAR BASE
|
||
|
#define kclsREGBL 'rgbl'
|
||
|
class REGBL : public REGBL_PAR
|
||
|
{
|
||
|
protected:
|
||
|
long _ypCur;
|
||
|
RC _rcRef;
|
||
|
RC _rc;
|
||
|
PGL _pglxp;
|
||
|
bool _fResize;
|
||
|
|
||
|
long _idypPrev;
|
||
|
long _idypCur;
|
||
|
long _ixpCur;
|
||
|
|
||
|
public:
|
||
|
REGBL(void)
|
||
|
{ _pglxp = pvNil; }
|
||
|
~REGBL(void)
|
||
|
{ ReleasePpo(&_pglxp); }
|
||
|
|
||
|
bool FInit(RC *prcRef, PGL pglxp = pvNil);
|
||
|
bool FStartRow(long dyp, long cxpMax);
|
||
|
void EndRow(void);
|
||
|
void AddXp(long xp)
|
||
|
{
|
||
|
// can only be called while building a row.
|
||
|
AssertThis(0);
|
||
|
Assert(_idypCur != ivNil, "calling AddXp outside a row");
|
||
|
Assert(_ixpCur < _pglxp->IvMac(), "overflow in AddXp");
|
||
|
|
||
|
if (_rc.xpLeft > xp)
|
||
|
_rc.xpLeft = xp;
|
||
|
if (_rc.xpRight < xp)
|
||
|
_rc.xpRight = xp;
|
||
|
*(long *)_pglxp->QvGet(_ixpCur++) = xp;
|
||
|
}
|
||
|
|
||
|
bool FDone(void)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
return _ypCur >= _rcRef.ypBottom && _idypCur == ivNil;
|
||
|
}
|
||
|
PGL PglxpFree(RC *prc, long *pdxp);
|
||
|
};
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Initialize a region builder.
|
||
|
***************************************************************************/
|
||
|
bool REGBL::FInit(RC *prcRef, PGL pglxp)
|
||
|
{
|
||
|
AssertVarMem(prcRef);
|
||
|
AssertNilOrPo(pglxp, 0);
|
||
|
Assert(!prcRef->FEmpty(), "empty reference rectangle");
|
||
|
|
||
|
ReleasePpo(&_pglxp);
|
||
|
if (pvNil != pglxp)
|
||
|
{
|
||
|
_pglxp = pglxp;
|
||
|
_pglxp->AddRef();
|
||
|
_fResize = fFalse;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pvNil == (_pglxp = GL::PglNew(size(long))))
|
||
|
return fFalse;
|
||
|
_pglxp->SetMinGrow(kcdxpBlock);
|
||
|
_fResize = fTrue;
|
||
|
}
|
||
|
|
||
|
_rcRef = *prcRef;
|
||
|
_ypCur = _rc.ypTop = _rc.ypBottom = prcRef->ypTop;
|
||
|
_rc.xpLeft = prcRef->Dxp();
|
||
|
_rc.xpRight = 0;
|
||
|
|
||
|
_idypPrev = _idypCur = ivNil;
|
||
|
_ixpCur = 0;
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Begin a row.
|
||
|
***************************************************************************/
|
||
|
bool REGBL::FStartRow(long dyp, long cxpMax)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
Assert(_idypCur == ivNil, "row already started");
|
||
|
AssertIn(dyp, 0, kcbMax);
|
||
|
Assert(_ypCur < _rcRef.ypBottom, "already filled");
|
||
|
AssertIn(cxpMax, 0, kcbMax);
|
||
|
long ivLim;
|
||
|
|
||
|
ivLim = _ixpCur + cxpMax + 2;
|
||
|
if (_fResize)
|
||
|
{
|
||
|
ivLim = LwRoundAway(ivLim, kcdxpBlock);
|
||
|
if (ivLim > _pglxp->IvMac() && !_pglxp->FSetIvMac(ivLim))
|
||
|
{
|
||
|
ReleasePpo(&_pglxp);
|
||
|
return fFalse;
|
||
|
}
|
||
|
}
|
||
|
else if (ivLim > _pglxp->IvMac())
|
||
|
{
|
||
|
Bug("overflowed provided pgl");
|
||
|
return fFalse;
|
||
|
}
|
||
|
|
||
|
dyp = LwMin(dyp, _rcRef.ypBottom - _ypCur);
|
||
|
_idypCur = _ixpCur;
|
||
|
*(long *)_pglxp->QvGet(_ixpCur++) = dyp;
|
||
|
_ypCur += dyp;
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
End a row in the region builder.
|
||
|
***************************************************************************/
|
||
|
void REGBL::EndRow(void)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
Assert((_ixpCur - _idypCur) & 1, "not an even number of xp values");
|
||
|
Assert(_ixpCur < _pglxp->IvMac(), "overflow in EndRow");
|
||
|
long czp;
|
||
|
long *qrgxp = (long *)_pglxp->QvGet(0);
|
||
|
|
||
|
qrgxp[_ixpCur++] = klwMax;
|
||
|
if ((czp = _ixpCur - _idypCur) > 2)
|
||
|
_rc.ypBottom = _ypCur;
|
||
|
|
||
|
// see if this row matches the last one
|
||
|
if (ivNil != _idypPrev && czp == _idypCur - _idypPrev &&
|
||
|
FEqualRgb(&qrgxp[_idypPrev + 1], &qrgxp[_idypCur + 1],
|
||
|
(czp - 1) * size(long)))
|
||
|
{
|
||
|
// this row matches the previous row
|
||
|
qrgxp[_idypPrev] += qrgxp[_idypCur];
|
||
|
_ixpCur = _idypCur;
|
||
|
}
|
||
|
else if (0 == _idypCur && _ixpCur == 2)
|
||
|
{
|
||
|
//at the top and still empty
|
||
|
_rc.ypTop = _rc.ypBottom = _ypCur;
|
||
|
_ixpCur = 0;
|
||
|
}
|
||
|
else
|
||
|
_idypPrev = _idypCur;
|
||
|
|
||
|
_idypCur = ivNil;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Clean up and return all the relevant information.
|
||
|
***************************************************************************/
|
||
|
PGL REGBL::PglxpFree(RC *prc, long *pdxp)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(prc);
|
||
|
AssertVarMem(pdxp);
|
||
|
PGL pglxp;
|
||
|
|
||
|
// see if the last row is empty
|
||
|
if (_ypCur > _rc.ypBottom)
|
||
|
{
|
||
|
// last row is empty
|
||
|
_ixpCur = _idypPrev;
|
||
|
}
|
||
|
|
||
|
*pdxp = 0;
|
||
|
if (0 == _ixpCur)
|
||
|
{
|
||
|
// empty
|
||
|
Assert(_rc.FEmpty(), 0);
|
||
|
ReleasePpo(&_pglxp);
|
||
|
_rc.Zero();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Assert(!_rc.FEmpty(), 0);
|
||
|
if (_ixpCur <= 4)
|
||
|
{
|
||
|
// rectangular
|
||
|
ReleasePpo(&_pglxp);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AssertDo(_pglxp->FSetIvMac(_ixpCur), 0);
|
||
|
AssertDo(_pglxp->FEnsureSpace(0, fgrpShrink), 0);
|
||
|
*pdxp = -_rc.xpLeft;
|
||
|
}
|
||
|
_rc.Offset(_rcRef.xpLeft, 0);
|
||
|
}
|
||
|
|
||
|
*prc = _rc;
|
||
|
pglxp = _pglxp;
|
||
|
_pglxp = pvNil;
|
||
|
return pglxp;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Constructor for the region scanner class.
|
||
|
***************************************************************************/
|
||
|
REGSC::REGSC(void)
|
||
|
{
|
||
|
_pglxpSrc = pvNil;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Destructor for the region scanner class.
|
||
|
***************************************************************************/
|
||
|
REGSC::~REGSC(void)
|
||
|
{
|
||
|
Free();
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Release our hold on any memory.
|
||
|
***************************************************************************/
|
||
|
void REGSC::Free(void)
|
||
|
{
|
||
|
if (pvNil != _pglxpSrc)
|
||
|
{
|
||
|
_pglxpSrc->Unlock();
|
||
|
ReleasePpo(&_pglxpSrc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Initializes a region scanner. The scanner implicitly intersects the
|
||
|
region with *prcRel and returns xp values relative to prcRel->xpLeft.
|
||
|
***************************************************************************/
|
||
|
void REGSC::Init(PREGN pregn, RC *prcRel)
|
||
|
{
|
||
|
RC rc = pregn->_rc;
|
||
|
rc.Offset(pregn->_dxp, 0);
|
||
|
_InitCore(pregn->_pglxp, &rc, prcRel);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Initializes a region scanner with the given rectangle.
|
||
|
***************************************************************************/
|
||
|
void REGSC::InitRc(RC *prc, RC *prcRel)
|
||
|
{
|
||
|
_InitCore(pvNil, prc, prcRel);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Initializes a region scanner. The scanner implicitly intersects the
|
||
|
region with *prcRel and returns xp values relative to prcRel->xpLeft.
|
||
|
***************************************************************************/
|
||
|
void REGSC::_InitCore(PGL pglxp, RC *prc, RC *prcRel)
|
||
|
{
|
||
|
Free();
|
||
|
|
||
|
RC rc = *prc;
|
||
|
if (pvNil != (_pglxpSrc = pglxp))
|
||
|
{
|
||
|
_pglxpSrc->AddRef();
|
||
|
_pxpLimCur = (long *)_pglxpSrc->PvLock(0);
|
||
|
_pxpLimSrc = _pxpLimCur + _pglxpSrc->IvMac();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rc.FIntersect(prcRel);
|
||
|
_rgxpRect[0] = rc.Dyp();
|
||
|
_rgxpRect[1] = 0;
|
||
|
_rgxpRect[2] = rc.Dxp();
|
||
|
_rgxpRect[3] = klwMax;
|
||
|
_pxpLimCur = _rgxpRect;
|
||
|
_pxpLimSrc = _rgxpRect + 4;
|
||
|
}
|
||
|
_dxp = rc.xpLeft - prcRel->xpLeft;
|
||
|
_xpRight = prcRel->Dxp();
|
||
|
|
||
|
_dyp = rc.ypTop - prcRel->ypTop;
|
||
|
_dypTot = prcRel->Dyp();
|
||
|
|
||
|
//initialize to an empty row
|
||
|
_pxpLimRow = (_pxpMinRow = _pxpLimCur) - 1;
|
||
|
_xpMinRow = klwMax;
|
||
|
|
||
|
ScanNext(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Scan the next horizontal strip of the region.
|
||
|
***************************************************************************/
|
||
|
void REGSC::_ScanNextCore(void)
|
||
|
{
|
||
|
long dxpT;
|
||
|
|
||
|
Assert(_dyp <= 0, "why is _ScanNextCore being called?");
|
||
|
if (_dypTot <= 0)
|
||
|
{
|
||
|
//ran out of region!
|
||
|
goto LEndRegion;
|
||
|
}
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
//do the next scan
|
||
|
if (_pxpLimCur >= _pxpLimSrc)
|
||
|
{
|
||
|
//ran out of region!
|
||
|
LEndRegion:
|
||
|
//use kswMax, not klwMax, so clients can add this to
|
||
|
//another yp value without fear of overflow
|
||
|
_dyp = kswMax;
|
||
|
_pxpLimRow = (_pxpMinRow = _pxpLimCur = _pxpLimSrc) - 1;
|
||
|
_xpMinRow = klwMax;
|
||
|
return;
|
||
|
}
|
||
|
if ((_dyp += *_pxpLimCur++) > 0)
|
||
|
break;
|
||
|
|
||
|
//find the start of the next row
|
||
|
while (*_pxpLimCur != klwMax)
|
||
|
{
|
||
|
_pxpLimCur += 2;
|
||
|
Assert(_pxpLimCur < _pxpLimSrc && _pxpLimCur[-1] != klwMax,
|
||
|
"bad region 1");
|
||
|
}
|
||
|
_pxpLimCur++;
|
||
|
}
|
||
|
|
||
|
//_pxpLimCur now points to the beginning of the correct row
|
||
|
//find the first xp value in our range
|
||
|
if (*_pxpLimCur < -_dxp)
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
_pxpLimCur += 2;
|
||
|
Assert(_pxpLimCur < _pxpLimSrc && _pxpLimCur[-1] != klwMax,
|
||
|
"bad region 2");
|
||
|
}
|
||
|
while (*_pxpLimCur < -_dxp);
|
||
|
|
||
|
//see if we went too far
|
||
|
if (_pxpLimCur[-1] > -_dxp)
|
||
|
{
|
||
|
_xpMinRow = 0;
|
||
|
_pxpMinRow = _pxpLimCur - 1;
|
||
|
goto LFindLim;
|
||
|
}
|
||
|
}
|
||
|
_xpMinRow = *_pxpLimCur++;
|
||
|
|
||
|
_pxpMinRow = _pxpLimCur;
|
||
|
if (_xpMinRow == klwMax)
|
||
|
{
|
||
|
//empty row
|
||
|
_pxpLimRow = _pxpLimCur - 1;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((_xpMinRow += _dxp) >= _xpRight)
|
||
|
{
|
||
|
//empty row, but we need to find the start of the next row
|
||
|
AssertIn(_xpMinRow, 0, kcbMax);
|
||
|
Assert(*_pxpLimCur != klwMax, "bad region 3");
|
||
|
|
||
|
//find the start of the next row
|
||
|
_pxpLimCur++;
|
||
|
while (*_pxpLimCur != klwMax)
|
||
|
{
|
||
|
_pxpLimCur += 2;
|
||
|
Assert(_pxpLimCur < _pxpLimSrc && _pxpLimCur[-1] != klwMax,
|
||
|
"bad region 4");
|
||
|
}
|
||
|
_pxpLimCur++;
|
||
|
_pxpLimRow = (_pxpMinRow = _pxpLimCur) - 1;
|
||
|
_xpMinRow = klwMax;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Assert(*_pxpLimCur != klwMax, "bad region 5");
|
||
|
_pxpLimCur++;
|
||
|
|
||
|
LFindLim:
|
||
|
dxpT = _xpRight - _dxp;
|
||
|
while (*_pxpLimCur < dxpT)
|
||
|
{
|
||
|
_pxpLimCur += 2;
|
||
|
Assert(_pxpLimCur < _pxpLimSrc && _pxpLimCur[-1] != klwMax,
|
||
|
"bad region 6");
|
||
|
}
|
||
|
_pxpLimRow = _pxpLimCur;
|
||
|
while (*_pxpLimCur != klwMax)
|
||
|
{
|
||
|
_pxpLimCur += 2;
|
||
|
Assert(_pxpLimCur < _pxpLimSrc && _pxpLimCur[-1] != klwMax,
|
||
|
"bad region 6");
|
||
|
}
|
||
|
_pxpLimCur++;
|
||
|
Assert((_pxpLimRow - _pxpMinRow) & 1, "logic error above");
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Static method to create a new region and set it to a rectangle.
|
||
|
***************************************************************************/
|
||
|
PREGN REGN::PregnNew(RC *prc)
|
||
|
{
|
||
|
PREGN pregn;
|
||
|
|
||
|
if (pvNil == (pregn = NewObj REGN))
|
||
|
return pvNil;
|
||
|
if (pvNil != prc)
|
||
|
pregn->_rc = *prc;
|
||
|
AssertPo(pregn, 0);
|
||
|
return pregn;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Destructor for a region.
|
||
|
***************************************************************************/
|
||
|
REGN::~REGN(void)
|
||
|
{
|
||
|
ReleasePpo(&_pglxp);
|
||
|
FreePhrgn(&_hrgn);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Make the region rectangular.
|
||
|
***************************************************************************/
|
||
|
void REGN::SetRc(RC *prc)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertNilOrVarMem(prc);
|
||
|
|
||
|
if (pvNil == prc)
|
||
|
_rc.Zero();
|
||
|
else
|
||
|
_rc = *prc;
|
||
|
ReleasePpo(&_pglxp);
|
||
|
FreePhrgn(&_hrgn);
|
||
|
_dxp = 0;
|
||
|
AssertThis(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Offset the region.
|
||
|
***************************************************************************/
|
||
|
void REGN::Offset(long xp, long yp)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
_rc.Offset(xp, yp);
|
||
|
_dptRgn.Offset(xp, yp);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return whether the region is empty and if prc is not nil, fill in *prc
|
||
|
with the region's bounding rectangle.
|
||
|
***************************************************************************/
|
||
|
bool REGN::FEmpty(RC *prc)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertNilOrVarMem(prc);
|
||
|
|
||
|
if (pvNil != prc)
|
||
|
*prc = _rc;
|
||
|
return _rc.FEmpty();
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Return whether the region is rectangular and if prc is not nil, fill in
|
||
|
*prc with the region's bounding rectangle.
|
||
|
***************************************************************************/
|
||
|
bool REGN::FIsRc(RC *prc)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
if (pvNil != prc)
|
||
|
*prc = _rc;
|
||
|
return _pglxp == pvNil;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Scale the x values of the region by the given amount.
|
||
|
***************************************************************************/
|
||
|
void REGN::Scale(long lwNumX, long lwDenX, long lwNumY, long lwDenY)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
Assert(lwDenX > 0 && lwNumX >= 0, "bad X scaling");
|
||
|
Assert(lwDenY > 0 && lwNumY >= 0, "bad Y scaling");
|
||
|
RC rcScaled;
|
||
|
long yp, ypScaled, dyp, dypScaled;
|
||
|
long xp1, xp2;
|
||
|
RAT ratXp(lwNumX, lwDenX);
|
||
|
RAT ratYp(lwNumY, lwDenY);
|
||
|
|
||
|
rcScaled.xpLeft = ratXp.LwScale(_rc.xpLeft);
|
||
|
rcScaled.xpRight = ratXp.LwScale(_rc.xpRight);
|
||
|
rcScaled.ypTop = ratYp.LwScale(_rc.ypTop);
|
||
|
rcScaled.ypBottom = ratYp.LwScale(_rc.ypBottom);
|
||
|
|
||
|
if (rcScaled.FEmpty())
|
||
|
{
|
||
|
_rc.Zero();
|
||
|
ReleasePpo(&_pglxp);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (pvNil == _pglxp)
|
||
|
{
|
||
|
_rc = rcScaled;
|
||
|
_dxp = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
REGSC regsc;
|
||
|
REGBL regbl;
|
||
|
|
||
|
regsc.Init(this, &_rc);
|
||
|
AssertDo(regbl.FInit(&rcScaled, _pglxp), 0);
|
||
|
yp = _rc.ypTop;
|
||
|
ypScaled = rcScaled.ypTop;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
dyp = regsc.DypCur();
|
||
|
dypScaled = ratYp.LwScale(yp + dyp) - ypScaled;
|
||
|
yp += dyp;
|
||
|
if (dypScaled <= 0)
|
||
|
{
|
||
|
regsc.ScanNext(dyp);
|
||
|
continue;
|
||
|
}
|
||
|
ypScaled += dypScaled;
|
||
|
|
||
|
AssertDo(regbl.FStartRow(dypScaled, regsc.CxpCur()), 0);
|
||
|
if (regsc.XpCur() == klwMax)
|
||
|
goto LEndRow;
|
||
|
|
||
|
xp1 = ratXp.LwScale(regsc.XpCur());
|
||
|
xp2 = ratXp.LwScale(regsc.XpFetch());
|
||
|
for (;;)
|
||
|
{
|
||
|
if (xp1 >= xp2)
|
||
|
{
|
||
|
// empty run - ignore both values
|
||
|
if (regsc.XpFetch() == klwMax)
|
||
|
break;
|
||
|
xp1 = ratXp.LwScale(regsc.XpCur());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// write xp1 and advance
|
||
|
regbl.AddXp(xp1);
|
||
|
xp1 = xp2;
|
||
|
}
|
||
|
|
||
|
// fetch a new xp2
|
||
|
if (regsc.XpFetch() == klwMax)
|
||
|
{
|
||
|
// write the last off
|
||
|
regbl.AddXp(xp1);
|
||
|
break;
|
||
|
}
|
||
|
xp2 = ratXp.LwScale(regsc.XpCur());
|
||
|
}
|
||
|
|
||
|
LEndRow:
|
||
|
regbl.EndRow();
|
||
|
if (regbl.FDone())
|
||
|
break;
|
||
|
|
||
|
regsc.ScanNext(dyp);
|
||
|
}
|
||
|
|
||
|
ReleasePpo(&_pglxp);
|
||
|
FreePhrgn(&_hrgn);
|
||
|
|
||
|
// force the region scanner to let go of the pglxp before the
|
||
|
// region builder tries to resize it.
|
||
|
regsc.Free();
|
||
|
|
||
|
_pglxp = regbl.PglxpFree(&_rc, &_dxp);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Union the two regions and leave the result in this one. If pregn2 is
|
||
|
nil, this region is used for pregn2.
|
||
|
***************************************************************************/
|
||
|
bool REGN::FUnion(PREGN pregn1, PREGN pregn2)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pregn1, 0);
|
||
|
AssertNilOrPo(pregn2, 0);
|
||
|
REGSC regsc1;
|
||
|
REGSC regsc2;
|
||
|
RC rc;
|
||
|
|
||
|
if (pvNil == pregn2)
|
||
|
{
|
||
|
if (pregn1->FEmpty())
|
||
|
return fTrue;
|
||
|
pregn2 = this;
|
||
|
}
|
||
|
|
||
|
rc.Union(&pregn1->_rc, &pregn2->_rc);
|
||
|
if (pvNil == pregn1->_pglxp && rc == pregn1->_rc ||
|
||
|
pvNil == pregn2->_pglxp && rc == pregn2->_rc)
|
||
|
{
|
||
|
// union is a rectangle
|
||
|
SetRc(&rc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
regsc1.Init(pregn1, &rc);
|
||
|
regsc2.Init(pregn2, &rc);
|
||
|
return _FUnionCore(&rc, ®sc1, ®sc2);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Union the given rectangle and region and leave the result in this region.
|
||
|
If pregn is nil, this region is used for pregn.
|
||
|
***************************************************************************/
|
||
|
bool REGN::FUnionRc(RC *prc, PREGN pregn)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(prc);
|
||
|
AssertNilOrPo(pregn, 0);
|
||
|
REGSC regsc1;
|
||
|
REGSC regsc2;
|
||
|
RC rc;
|
||
|
|
||
|
if (pvNil == pregn)
|
||
|
{
|
||
|
if (prc->FEmpty())
|
||
|
return fTrue;
|
||
|
pregn = this;
|
||
|
}
|
||
|
|
||
|
rc.Union(prc, &pregn->_rc);
|
||
|
if (rc == *prc)
|
||
|
{
|
||
|
// *prc totally contains the region
|
||
|
SetRc(&rc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
// if this one is rectangular and already contains *prc, then
|
||
|
// we're done
|
||
|
if (this == pregn && pvNil == _pglxp && rc == _rc)
|
||
|
return fTrue;
|
||
|
|
||
|
regsc1.InitRc(prc, &rc);
|
||
|
regsc2.Init(pregn, &rc);
|
||
|
return _FUnionCore(&rc, ®sc1, ®sc2);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Core union routine.
|
||
|
***************************************************************************/
|
||
|
bool REGN::_FUnionCore(RC *prc, PREGSC pregsc1, PREGSC pregsc2)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pregsc1, 0);
|
||
|
AssertPo(pregsc2, 0);
|
||
|
void *pvSwap;
|
||
|
long dyp;
|
||
|
REGBL regbl;
|
||
|
|
||
|
if (!regbl.FInit(prc))
|
||
|
return fFalse;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
dyp = LwMin(pregsc1->DypCur(), pregsc2->DypCur());
|
||
|
if (!regbl.FStartRow(dyp, pregsc1->CxpCur() + pregsc2->CxpCur()))
|
||
|
return fFalse;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
Assert(FPure(pregsc1->FOn()) == FPure(pregsc2->FOn()),
|
||
|
"region scanners have different on/off states");
|
||
|
if (pregsc1->XpCur() > pregsc2->XpCur())
|
||
|
{
|
||
|
//swap them
|
||
|
pvSwap = pregsc1;
|
||
|
pregsc1 = pregsc2;
|
||
|
pregsc2 = (PREGSC)pvSwap;
|
||
|
}
|
||
|
if (pregsc2->XpCur() == klwMax)
|
||
|
break;
|
||
|
|
||
|
if (pregsc1->FOn())
|
||
|
{
|
||
|
//both on
|
||
|
regbl.AddXp(pregsc1->XpCur());
|
||
|
pregsc1->XpFetch();
|
||
|
//1 off, 2 on
|
||
|
if (pregsc1->XpCur() < pregsc2->XpCur())
|
||
|
{
|
||
|
regbl.AddXp(pregsc1->XpCur());
|
||
|
pregsc1->XpFetch();
|
||
|
continue;
|
||
|
}
|
||
|
pregsc2->XpFetch();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//both off
|
||
|
pregsc1->XpFetch();
|
||
|
//1 on, 2 off
|
||
|
if (pregsc1->XpCur() <= pregsc2->XpCur())
|
||
|
{
|
||
|
pregsc1->XpFetch();
|
||
|
continue;
|
||
|
}
|
||
|
regbl.AddXp(pregsc2->XpCur());
|
||
|
pregsc2->XpFetch();
|
||
|
}
|
||
|
}
|
||
|
Assert(pregsc1->FOn(), "bad regions");
|
||
|
Assert(pregsc2->XpCur() == klwMax, 0);
|
||
|
|
||
|
//copy the remainder of this row from pregsc1
|
||
|
while (pregsc1->XpCur() < klwMax)
|
||
|
{
|
||
|
regbl.AddXp(pregsc1->XpCur());
|
||
|
pregsc1->XpFetch();
|
||
|
}
|
||
|
Assert(pregsc1->FOn(), "bad region");
|
||
|
|
||
|
regbl.EndRow();
|
||
|
if (regbl.FDone())
|
||
|
break;
|
||
|
|
||
|
pregsc1->ScanNext(dyp);
|
||
|
pregsc2->ScanNext(dyp);
|
||
|
}
|
||
|
|
||
|
ReleasePpo(&_pglxp);
|
||
|
FreePhrgn(&_hrgn);
|
||
|
|
||
|
_pglxp = regbl.PglxpFree(&_rc, &_dxp);
|
||
|
AssertThis(0);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Intersect the two regions and leave the result in this one. If pregn2
|
||
|
is nil, this region is used for pregn2.
|
||
|
***************************************************************************/
|
||
|
bool REGN::FIntersect(PREGN pregn1, PREGN pregn2)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pregn1, 0);
|
||
|
AssertNilOrPo(pregn2, 0);
|
||
|
REGSC regsc1;
|
||
|
REGSC regsc2;
|
||
|
RC rc;
|
||
|
|
||
|
if (pvNil == pregn2)
|
||
|
pregn2 = this;
|
||
|
|
||
|
if (!rc.FIntersect(&pregn1->_rc, &pregn2->_rc))
|
||
|
{
|
||
|
// result is empty
|
||
|
SetRc(&rc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
// if pregn1 is rectangular and contains this one, then
|
||
|
// we're done
|
||
|
if (this == pregn2 && pvNil == pregn1->_pglxp && rc == _rc)
|
||
|
return fTrue;
|
||
|
if (pvNil == pregn1->_pglxp && pvNil == pregn2->_pglxp)
|
||
|
{
|
||
|
//both rectangles, so the intersection is a rectangle
|
||
|
SetRc(&rc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
regsc1.Init(pregn1, &rc);
|
||
|
regsc2.Init(pregn2, &rc);
|
||
|
return _FIntersectCore(&rc, ®sc1, ®sc2);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Intersect the given rectangle and region and leave the result in this
|
||
|
region. If pregn is nil, this region is used for pregn.
|
||
|
***************************************************************************/
|
||
|
bool REGN::FIntersectRc(RC *prc, PREGN pregn)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(prc);
|
||
|
AssertNilOrPo(pregn, 0);
|
||
|
REGSC regsc1;
|
||
|
REGSC regsc2;
|
||
|
RC rc;
|
||
|
|
||
|
if (pvNil == pregn)
|
||
|
pregn = this;
|
||
|
|
||
|
if (!rc.FIntersect(prc, &pregn->_rc))
|
||
|
{
|
||
|
// result is empty
|
||
|
SetRc(&rc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
// if *prc contains this region, then we're done
|
||
|
if (this == pregn && rc == _rc)
|
||
|
return fTrue;
|
||
|
if (pvNil == pregn->_pglxp)
|
||
|
{
|
||
|
//both rectangles, so the intersection is a rectangle
|
||
|
SetRc(&rc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
regsc1.InitRc(prc, &rc);
|
||
|
regsc2.Init(pregn, &rc);
|
||
|
return _FIntersectCore(&rc, ®sc1, ®sc2);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Core intersect routine.
|
||
|
***************************************************************************/
|
||
|
bool REGN::_FIntersectCore(RC *prc, PREGSC pregsc1, PREGSC pregsc2)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pregsc1, 0);
|
||
|
AssertPo(pregsc2, 0);
|
||
|
long dyp;
|
||
|
void *pvSwap;
|
||
|
REGBL regbl;
|
||
|
|
||
|
if (!regbl.FInit(prc))
|
||
|
return fFalse;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
dyp = LwMin(pregsc1->DypCur(), pregsc2->DypCur());
|
||
|
if (!regbl.FStartRow(dyp, pregsc1->CxpCur() + pregsc2->CxpCur()))
|
||
|
return fFalse;
|
||
|
|
||
|
while (pregsc1->XpCur() < klwMax && pregsc2->XpCur() < klwMax)
|
||
|
{
|
||
|
Assert(FPure(pregsc1->FOn()) == FPure(pregsc2->FOn()),
|
||
|
"region scanners have different on/off states");
|
||
|
if (pregsc1->XpCur() > pregsc2->XpCur())
|
||
|
{
|
||
|
//swap them
|
||
|
pvSwap = pregsc1;
|
||
|
pregsc1 = pregsc2;
|
||
|
pregsc2 = (PREGSC)pvSwap;
|
||
|
}
|
||
|
|
||
|
//NOTE: this is pretty much the adjoint of what's in _FUnionCore - ie,
|
||
|
//swap the sense of FOn
|
||
|
if (pregsc1->FOn())
|
||
|
{
|
||
|
//both on
|
||
|
pregsc1->XpFetch();
|
||
|
//1 off, 2 on
|
||
|
if (pregsc1->XpCur() <= pregsc2->XpCur())
|
||
|
{
|
||
|
pregsc1->XpFetch();
|
||
|
continue;
|
||
|
}
|
||
|
regbl.AddXp(pregsc2->XpCur());
|
||
|
pregsc2->XpFetch();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//both off
|
||
|
regbl.AddXp(pregsc1->XpCur());
|
||
|
pregsc1->XpFetch();
|
||
|
//1 on, 2 off
|
||
|
if (pregsc1->XpCur() < pregsc2->XpCur())
|
||
|
{
|
||
|
regbl.AddXp(pregsc1->XpCur());
|
||
|
pregsc1->XpFetch();
|
||
|
continue;
|
||
|
}
|
||
|
pregsc2->XpFetch();
|
||
|
}
|
||
|
}
|
||
|
Assert(pregsc1->FOn(), "bad regions");
|
||
|
regbl.EndRow();
|
||
|
if (regbl.FDone())
|
||
|
break;
|
||
|
|
||
|
pregsc1->ScanNext(dyp);
|
||
|
pregsc2->ScanNext(dyp);
|
||
|
}
|
||
|
|
||
|
ReleasePpo(&_pglxp);
|
||
|
FreePhrgn(&_hrgn);
|
||
|
|
||
|
_pglxp = regbl.PglxpFree(&_rc, &_dxp);
|
||
|
AssertThis(0);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Compute regn2 minus regn1 and put the result in this region. If
|
||
|
pregn2 is nil, this region is used.
|
||
|
***************************************************************************/
|
||
|
bool REGN::FDiff(PREGN pregn1, PREGN pregn2)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pregn1, 0);
|
||
|
AssertNilOrPo(pregn2, 0);
|
||
|
REGSC regsc1;
|
||
|
REGSC regsc2;
|
||
|
RC rc, rcT;
|
||
|
|
||
|
if (pvNil == pregn2)
|
||
|
{
|
||
|
// use this region as the first operand
|
||
|
if (!rc.FIntersect(&_rc, &pregn1->_rc))
|
||
|
return fTrue;
|
||
|
pregn2 = this;
|
||
|
}
|
||
|
|
||
|
if (pregn2->_rc.FEmpty())
|
||
|
{
|
||
|
rc.Zero();
|
||
|
SetRc(&rc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
rc = pregn2->_rc;
|
||
|
regsc1.Init(pregn1, &rc);
|
||
|
regsc2.Init(pregn2, &rc);
|
||
|
return _FDiffCore(&rc, ®sc2, ®sc1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Compute regn minus the given rectangle and put the result in this region.
|
||
|
If pregn is nil, this region is used.
|
||
|
***************************************************************************/
|
||
|
bool REGN::FDiffRc(RC *prc, PREGN pregn)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(prc);
|
||
|
AssertNilOrPo(pregn, 0);
|
||
|
REGSC regsc1;
|
||
|
REGSC regsc2;
|
||
|
RC rc;
|
||
|
|
||
|
if (pvNil == pregn)
|
||
|
{
|
||
|
if (!rc.FIntersect(&_rc, prc))
|
||
|
return fTrue;
|
||
|
pregn = this;
|
||
|
}
|
||
|
|
||
|
if (pregn->_rc.FEmpty() || prc->FContains(&pregn->_rc))
|
||
|
{
|
||
|
// the rectangle contains the entire region or the region is empty
|
||
|
rc.Zero();
|
||
|
SetRc(&rc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
rc = pregn->_rc;
|
||
|
regsc1.InitRc(prc, &rc);
|
||
|
regsc2.Init(pregn, &rc);
|
||
|
return _FDiffCore(&rc, ®sc2, ®sc1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Compute *prc minus the given region and put the result in this region.
|
||
|
If pregn is nil, this region is used.
|
||
|
***************************************************************************/
|
||
|
bool REGN::FDiffFromRc(RC *prc, PREGN pregn)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertVarMem(prc);
|
||
|
AssertNilOrPo(pregn, 0);
|
||
|
REGSC regsc1;
|
||
|
REGSC regsc2;
|
||
|
RC rc;
|
||
|
|
||
|
if (pvNil == pregn)
|
||
|
pregn = this;
|
||
|
|
||
|
if (!rc.FIntersect(prc, &pregn->_rc))
|
||
|
{
|
||
|
SetRc(prc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
if (pvNil == pregn->_pglxp && pregn->_rc.FContains(prc))
|
||
|
{
|
||
|
// the region is rectangle and contains *prc, so the diff is empty
|
||
|
rc.Zero();
|
||
|
SetRc(&rc);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
regsc1.InitRc(prc, prc);
|
||
|
regsc2.Init(pregn, prc);
|
||
|
return _FDiffCore(prc, ®sc1, ®sc2);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Core diff routine. Compute pregsc1 minus pregsc2.
|
||
|
***************************************************************************/
|
||
|
bool REGN::_FDiffCore(RC *prc, PREGSC pregsc1, PREGSC pregsc2)
|
||
|
{
|
||
|
AssertThis(0);
|
||
|
AssertPo(pregsc1, 0);
|
||
|
AssertPo(pregsc2, 0);
|
||
|
long dyp;
|
||
|
long xp1, xp2;
|
||
|
REGBL regbl;
|
||
|
|
||
|
if (!regbl.FInit(prc))
|
||
|
return fFalse;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
dyp = LwMin(pregsc1->DypCur(), pregsc2->DypCur());
|
||
|
if (!regbl.FStartRow(dyp, pregsc1->CxpCur() + pregsc2->CxpCur()))
|
||
|
return fFalse;
|
||
|
|
||
|
xp1 = pregsc1->XpCur();
|
||
|
xp2 = pregsc2->XpCur();
|
||
|
while (xp1 < klwMax)
|
||
|
{
|
||
|
if (pregsc1->FOn())
|
||
|
{
|
||
|
while (xp2 <= xp1)
|
||
|
xp2 = pregsc2->XpFetch();
|
||
|
|
||
|
// xp1 < xp2
|
||
|
if (pregsc2->FOn())
|
||
|
{
|
||
|
// both on
|
||
|
regbl.AddXp(xp1);
|
||
|
}
|
||
|
xp1 = pregsc1->XpFetch();
|
||
|
}
|
||
|
|
||
|
Assert(!pregsc1->FOn(), "why are we here?");
|
||
|
if (!pregsc2->FOn())
|
||
|
{
|
||
|
// both off
|
||
|
if (xp1 <= xp2)
|
||
|
{
|
||
|
xp1 = pregsc1->XpFetch();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// xp2 < xp1
|
||
|
regbl.AddXp(xp2);
|
||
|
xp2 = pregsc2->XpFetch();
|
||
|
}
|
||
|
|
||
|
Assert(!pregsc1->FOn() && pregsc2->FOn(), "we shouldn't be here");
|
||
|
// 1 off, 2 on
|
||
|
if (xp2 < xp1)
|
||
|
{
|
||
|
regbl.AddXp(xp2);
|
||
|
xp2 = pregsc2->XpFetch();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
regbl.AddXp(xp1);
|
||
|
xp1 = pregsc1->XpFetch();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Assert(pregsc1->FOn(), "bad region");
|
||
|
regbl.EndRow();
|
||
|
if (regbl.FDone())
|
||
|
break;
|
||
|
|
||
|
pregsc1->ScanNext(dyp);
|
||
|
pregsc2->ScanNext(dyp);
|
||
|
}
|
||
|
|
||
|
ReleasePpo(&_pglxp);
|
||
|
FreePhrgn(&_hrgn);
|
||
|
|
||
|
_pglxp = regbl.PglxpFree(&_rc, &_dxp);
|
||
|
AssertThis(0);
|
||
|
return fTrue;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Create a system region that is equivalent to the given region.
|
||
|
***************************************************************************/
|
||
|
HRGN REGN::HrgnCreate(void)
|
||
|
{
|
||
|
#ifdef WIN
|
||
|
RGNDATAHEADER *prd;
|
||
|
RCS *prcs;
|
||
|
REGSC regsc;
|
||
|
long crcMac, crc;
|
||
|
HRGN hrgn;
|
||
|
long yp, dyp;
|
||
|
|
||
|
crcMac = _pglxp == pvNil ? 1 : _pglxp->IvMac() / 2;
|
||
|
if (!FAllocPv((void **)&prd, size(RGNDATAHEADER) + LwMul(crcMac, size(RECT)),
|
||
|
fmemNil, mprNormal))
|
||
|
{
|
||
|
return hNil;
|
||
|
}
|
||
|
|
||
|
prcs = (RCS *)(prd + 1);
|
||
|
regsc.Init(this, &_rc);
|
||
|
crc = 0;
|
||
|
if (!_rc.FEmpty())
|
||
|
{
|
||
|
yp = _rc.ypTop;
|
||
|
for (;;)
|
||
|
{
|
||
|
dyp = regsc.DypCur();
|
||
|
while (regsc.XpCur() < klwMax)
|
||
|
{
|
||
|
prcs->left = regsc.XpCur() + _rc.xpLeft;
|
||
|
prcs->right = regsc.XpFetch() + _rc.xpLeft;
|
||
|
regsc.XpFetch();
|
||
|
prcs->top = yp;
|
||
|
prcs->bottom = yp + dyp;
|
||
|
prcs++;
|
||
|
crc++;
|
||
|
}
|
||
|
if ((yp += dyp) >= _rc.ypBottom)
|
||
|
break;
|
||
|
regsc.ScanNext(dyp);
|
||
|
}
|
||
|
}
|
||
|
AssertIn(crc, 0, crcMac + 1);
|
||
|
prd->dwSize = size(RGNDATAHEADER);
|
||
|
prd->iType = RDH_RECTANGLES;
|
||
|
prd->nCount = crc;
|
||
|
prd->nRgnSize = 0;
|
||
|
prd->rcBound = (RCS)_rc;
|
||
|
|
||
|
hrgn = ExtCreateRegion(pvNil, size(RGNDATAHEADER) + LwMul(crc, size(RECT)),
|
||
|
(RGNDATA *)prd);
|
||
|
FreePpv((void **)&prd);
|
||
|
return hrgn;
|
||
|
#endif //WIN
|
||
|
#ifdef MAC
|
||
|
HRGN hrgn;
|
||
|
|
||
|
if (pvNil == _pglxp)
|
||
|
{
|
||
|
RCS rcs;
|
||
|
|
||
|
if (hNil == (hrgn = NewRgn()))
|
||
|
return hNil;
|
||
|
rcs = RCS(_rc);
|
||
|
RectRgn(hrgn, &rcs);
|
||
|
return hrgn;
|
||
|
}
|
||
|
|
||
|
REGSC regsc1;
|
||
|
REGSC regsc2;
|
||
|
RC rc;
|
||
|
long yp;
|
||
|
long cb;
|
||
|
long xp1, xp2;
|
||
|
short *psw;
|
||
|
|
||
|
regsc1.Init(this, &_rc);
|
||
|
rc = _rc;
|
||
|
rc.ypTop--;
|
||
|
regsc2.Init(this, &rc);
|
||
|
for (yp = _rc.ypTop, cb = size(Region); ; )
|
||
|
{
|
||
|
cb += size(short);
|
||
|
xp1 = regsc1.XpCur();
|
||
|
xp2 = regsc2.XpCur();
|
||
|
for (;;)
|
||
|
{
|
||
|
if (xp1 == xp2)
|
||
|
{
|
||
|
if (xp1 == klwMax)
|
||
|
break;
|
||
|
xp1 = regsc1.XpFetch();
|
||
|
xp2 = regsc2.XpFetch();
|
||
|
continue;
|
||
|
}
|
||
|
else if (xp1 < xp2)
|
||
|
{
|
||
|
cb += size(short);
|
||
|
xp1 = regsc1.XpFetch();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cb += size(short);
|
||
|
xp2 = regsc2.XpFetch();
|
||
|
}
|
||
|
}
|
||
|
cb += size(short);
|
||
|
if (yp >= _rc.ypBottom)
|
||
|
break;
|
||
|
yp += regsc1.DypCur();
|
||
|
regsc1.ScanNext(regsc1.DypCur());
|
||
|
regsc2.ScanNext(regsc2.DypCur());
|
||
|
}
|
||
|
cb += size(short);
|
||
|
|
||
|
if (hNil == (hrgn = (HRGN)NewHandle(cb)))
|
||
|
return hNil;
|
||
|
|
||
|
HLock((HN)hrgn);
|
||
|
(*hrgn)->rgnSize = (short)cb;
|
||
|
(*hrgn)->rgnBBox = RCS(_rc);
|
||
|
|
||
|
regsc1.Init(this, &_rc);
|
||
|
rc = _rc;
|
||
|
rc.ypTop--;
|
||
|
regsc2.Init(this, &rc);
|
||
|
for (yp = _rc.ypTop, psw = (short *)((*hrgn) + 1); ; )
|
||
|
{
|
||
|
*psw++ = (short)yp;
|
||
|
xp1 = regsc1.XpCur();
|
||
|
xp2 = regsc2.XpCur();
|
||
|
for (;;)
|
||
|
{
|
||
|
if (xp1 == xp2)
|
||
|
{
|
||
|
if (xp1 == klwMax)
|
||
|
break;
|
||
|
xp1 = regsc1.XpFetch();
|
||
|
xp2 = regsc2.XpFetch();
|
||
|
continue;
|
||
|
}
|
||
|
else if (xp1 < xp2)
|
||
|
{
|
||
|
*psw++ = (short)xp1;
|
||
|
xp1 = regsc1.XpFetch();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*psw++ = (short)xp2;
|
||
|
xp2 = regsc2.XpFetch();
|
||
|
}
|
||
|
}
|
||
|
*psw++ = 0x7FFF;
|
||
|
if (yp >= _rc.ypBottom)
|
||
|
break;
|
||
|
yp += regsc1.DypCur();
|
||
|
regsc1.ScanNext(regsc1.DypCur());
|
||
|
regsc2.ScanNext(regsc2.DypCur());
|
||
|
}
|
||
|
*psw++ = 0x7FFF;
|
||
|
Assert(psw == PvAddBv(*hrgn, cb), "wrong size!");
|
||
|
HUnlock((HN)hrgn);
|
||
|
return hrgn;
|
||
|
#endif //MAC
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
If we don't have a cached HRGN equivalent of this region, create one.
|
||
|
In either case return it.
|
||
|
***************************************************************************/
|
||
|
HRGN REGN::HrgnEnsure(void)
|
||
|
{
|
||
|
if (hNil != _hrgn)
|
||
|
{
|
||
|
if (_dptRgn.xp != 0 || _dptRgn.yp != 0)
|
||
|
{
|
||
|
OffsetRgn(_hrgn, (short)_dptRgn.xp, (short)_dptRgn.yp);
|
||
|
_dptRgn.xp = _dptRgn.yp = 0;
|
||
|
}
|
||
|
return _hrgn;
|
||
|
}
|
||
|
|
||
|
_dptRgn.xp = _dptRgn.yp = 0;
|
||
|
_hrgn = HrgnCreate();
|
||
|
return _hrgn;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/***************************************************************************
|
||
|
Assert the validity of a REGN.
|
||
|
***************************************************************************/
|
||
|
void REGN::AssertValid(ulong grf)
|
||
|
{
|
||
|
REGN_PAR::AssertValid(0);
|
||
|
AssertNilOrPo(_pglxp, 0);
|
||
|
Assert(_dxp <= 0, "bad _dxp");
|
||
|
Assert(_dxp == 0 || _pglxp != pvNil, "_dxp should be zero");
|
||
|
//REVIEW shonk: REGN::AssertValid: fill this in
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
Mark memory for the REGN.
|
||
|
***************************************************************************/
|
||
|
void REGN::MarkMem(void)
|
||
|
{
|
||
|
AssertValid(0);
|
||
|
REGN_PAR::MarkMem();
|
||
|
MarkMemObj(_pglxp);
|
||
|
}
|
||
|
#endif //DEBUG
|
||
|
|
||
|
|