3068 lines
74 KiB
C++
3068 lines
74 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Reviewed:
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
GFX classes: graphics device (GDV), graphics environment (GNV)
|
|
|
|
***************************************************************************/
|
|
#include "frame.h"
|
|
ASSERTNAME
|
|
|
|
|
|
APT vaptGray = { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 };
|
|
APT vaptLtGray = { 0x22, 0x88, 0x44, 0x11, 0x22, 0x88, 0x44, 0x11 };
|
|
APT vaptDkGray = { 0xDD, 0x77, 0xBB, 0xEE, 0xDD, 0x77, 0xBB, 0xEE };
|
|
|
|
NTL vntl;
|
|
|
|
|
|
RTCLASS(GNV)
|
|
RTCLASS(GPT)
|
|
RTCLASS(NTL)
|
|
RTCLASS(OGN)
|
|
|
|
|
|
const long kdtsMaxTrans = 30 * kdtsSecond;
|
|
long vcactRealize;
|
|
|
|
|
|
/***************************************************************************
|
|
Set the ACR from the lw. The lw should have been returned by a call
|
|
to ACR::LwGet().
|
|
***************************************************************************/
|
|
void ACR::SetFromLw(long lw)
|
|
{
|
|
_lu = (ulong)lw;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get an lw from the ACR. The lw can then be stored on file, reread
|
|
and passed to ACR::SetFromLw. Valid non-nil colors always return
|
|
non-zero, so zero can be used as a nil value.
|
|
***************************************************************************/
|
|
long ACR::LwGet(void) const
|
|
{
|
|
return (long)_lu;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a clr from the ACR. Asserts that the acr is an rgb color.
|
|
***************************************************************************/
|
|
void ACR::GetClr(CLR *pclr)
|
|
{
|
|
AssertThis(facrRgb);
|
|
AssertVarMem(pclr);
|
|
|
|
pclr->bRed = B2Lw(_lu);
|
|
pclr->bGreen = B1Lw(_lu);
|
|
pclr->bBlue = B0Lw(_lu);
|
|
pclr->bZero = 0;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert that the acr is a valid color.
|
|
***************************************************************************/
|
|
void ACR::AssertValid(ulong grfacr)
|
|
{
|
|
switch (B3Lw(_lu))
|
|
{
|
|
case kbNilAcr:
|
|
Assert(grfacr == facrNil, "unexpected nil ACR");
|
|
break;
|
|
case kbRgbAcr:
|
|
Assert(grfacr == facrNil || (grfacr & facrRgb), "unexpected RGB ACR");
|
|
break;
|
|
case kbIndexAcr:
|
|
Assert(B2Lw(_lu) == 0 && B1Lw(_lu) == 0, "bad Index ACR");
|
|
Assert(grfacr == facrNil || (grfacr & facrIndex), "unexpected Index ACR");
|
|
break;
|
|
case kbSpecialAcr:
|
|
Assert(_lu == kluAcrClear || _lu == kluAcrInvert, "unknown acr");
|
|
Assert(grfacr == facrNil, "unexpected Special ACR");
|
|
break;
|
|
default:
|
|
BugVar("invalid ACR", &_lu);
|
|
break;
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Change the origin on the pattern.
|
|
***************************************************************************/
|
|
void APT::MoveOrigin(long dxp, long dyp)
|
|
{
|
|
//this cast to ulong works because 2^32 is a multiple of 8.
|
|
dxp = (ulong)dxp % 8;
|
|
dyp = (ulong)dyp % 8;
|
|
if (dxp != 0)
|
|
{
|
|
byte *pb;
|
|
|
|
for (pb = rgb + 8; pb-- != rgb; )
|
|
*pb = (*pb >> dxp) | (*pb << (8 - dxp));
|
|
}
|
|
if (dyp != 0)
|
|
SwapBlocks(rgb, 8 - dyp, dyp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for Graphics environment.
|
|
***************************************************************************/
|
|
GNV::GNV(GPT *pgpt)
|
|
{
|
|
AssertPo(pgpt, 0);
|
|
|
|
_Init(pgpt);
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for Graphics environment based on a pgob.
|
|
***************************************************************************/
|
|
GNV::GNV(PGOB pgob)
|
|
{
|
|
AssertPo(pgob, 0);
|
|
|
|
_Init(pgob->Pgpt()); //use the GOB's port
|
|
SetGobRc(pgob); //set the rc's according to the gob
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for Graphics environment based on both a port and a pgob.
|
|
***************************************************************************/
|
|
GNV::GNV(PGOB pgob, PGPT pgpt)
|
|
{
|
|
AssertPo(pgpt, 0);
|
|
AssertPo(pgob, 0);
|
|
|
|
_Init(pgpt);
|
|
SetGobRc(pgob);
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the GNV.
|
|
***************************************************************************/
|
|
GNV::~GNV(void)
|
|
{
|
|
AssertThis(0);
|
|
Mac( _pgpt->Unlock(); )
|
|
ReleasePpo(&_pgpt);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fill in all fields of the gnv with default values.
|
|
***************************************************************************/
|
|
void GNV::_Init(PGPT pgpt)
|
|
{
|
|
PT pt;
|
|
|
|
_pgpt = pgpt;
|
|
pgpt->AddRef();
|
|
Mac( pgpt->Lock(); )
|
|
_rcSrc.Set(0, 0, 1, 1);
|
|
pgpt->GetPtBase(&pt);
|
|
_rcDst.OffsetCopy(&_rcSrc, -pt.xp, -pt.yp);
|
|
_xp = _yp = 0;
|
|
|
|
_dsf.onn = vntl.OnnSystem();
|
|
_dsf.grfont = fontNil;
|
|
_dsf.dyp = vpappb->DypTextDef();
|
|
_dsf.tah = tahLeft;
|
|
_dsf.tav = tavTop;
|
|
|
|
_gdd.dxpPen = _gdd.dypPen = 1;
|
|
_gdd.prcsClip = pvNil;
|
|
_rcVis.Max();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the mapping and vis according to the gob.
|
|
***************************************************************************/
|
|
void GNV::SetGobRc(PGOB pgob)
|
|
{
|
|
RC rc;
|
|
|
|
//set the mapping
|
|
pgob->GetRc(&rc, cooGpt);
|
|
SetRcDst(&rc);
|
|
pgob->GetRc(&rc, cooLocal);
|
|
SetRcSrc(&rc);
|
|
pgob->GetRcVis(&rc, cooLocal);
|
|
SetRcVis(&rc);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the gnv
|
|
***************************************************************************/
|
|
void GNV::AssertValid(ulong grf)
|
|
{
|
|
GNV_PAR::AssertValid(0);
|
|
AssertPo(_pgpt, 0);
|
|
AssertPo(&_dsf, 0);
|
|
Assert(!_rcSrc.FEmpty(), "empty src rectangle");
|
|
Assert(!_rcDst.FEmpty(), "empty dst rectangle");
|
|
Assert(_gdd.prcsClip == pvNil || _gdd.prcsClip == &_rcsClip,
|
|
"bad _gdd.prcsClip");
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the GNV.
|
|
***************************************************************************/
|
|
void GNV::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
GNV_PAR::MarkMem();
|
|
MarkMemObj(_pgpt);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Fill a rectangle with a two color pattern.
|
|
***************************************************************************/
|
|
void GNV::FillRcApt(RC *prc, APT *papt, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertVarMem(papt);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs))
|
|
return;
|
|
|
|
_gdd.grfgdd = fgddFill | fgddPattern;
|
|
_gdd.apt = *papt;
|
|
_gdd.apt.MoveOrigin(_rcDst.xpLeft - _rcSrc.xpLeft, _rcDst.ypTop - _rcSrc.ypTop);
|
|
_gdd.acrFore = acrFore;
|
|
_gdd.acrBack = acrBack;
|
|
|
|
_pgpt->DrawRcs(&rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fill a rectangle with a color.
|
|
***************************************************************************/
|
|
void GNV::FillRc(RC *prc, ACR acr)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertPo(&acr, 0);
|
|
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs))
|
|
return;
|
|
|
|
_gdd.grfgdd = fgddFill;
|
|
_gdd.acrFore = acr;
|
|
|
|
_pgpt->DrawRcs(&rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Frame a rectangle with a two color pattern.
|
|
***************************************************************************/
|
|
void GNV::FrameRcApt(RC *prc, APT *papt, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertVarMem(papt);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs) || _gdd.dxpPen == 0 && _gdd.dypPen == 0)
|
|
return;
|
|
|
|
_gdd.grfgdd = fgddFrame | fgddPattern;
|
|
_gdd.apt = *papt;
|
|
_gdd.apt.MoveOrigin(_rcDst.xpLeft - _rcSrc.xpLeft, _rcDst.ypTop - _rcSrc.ypTop);
|
|
_gdd.acrFore = acrFore;
|
|
_gdd.acrBack = acrBack;
|
|
|
|
_pgpt->DrawRcs(&rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Frame a rectangle with a color.
|
|
***************************************************************************/
|
|
void GNV::FrameRc(RC *prc, ACR acr)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertPo(&acr, 0);
|
|
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs) || _gdd.dxpPen == 0 && _gdd.dypPen == 0)
|
|
return;
|
|
|
|
_gdd.grfgdd = fgddFrame;
|
|
_gdd.acrFore = acr;
|
|
|
|
_pgpt->DrawRcs(&rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
For hilighting text. On mac, interchanges the system hilite color and
|
|
the background color. On Win, just inverts.
|
|
***************************************************************************/
|
|
void GNV::HiliteRc(RC *prc, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs))
|
|
return;
|
|
_gdd.acrBack = acrBack;
|
|
_pgpt->HiliteRcs(&rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fill an oval with a two color pattern.
|
|
***************************************************************************/
|
|
void GNV::FillOvalApt(RC *prc, APT *papt, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertVarMem(papt);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs))
|
|
return;
|
|
|
|
_gdd.grfgdd = fgddFill | fgddPattern;
|
|
_gdd.apt = *papt;
|
|
_gdd.apt.MoveOrigin(_rcDst.xpLeft - _rcSrc.xpLeft, _rcDst.ypTop - _rcSrc.ypTop);
|
|
_gdd.acrFore = acrFore;
|
|
_gdd.acrBack = acrBack;
|
|
|
|
_pgpt->DrawOval(&rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fill an oval with a color.
|
|
***************************************************************************/
|
|
void GNV::FillOval(RC *prc, ACR acr)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertPo(&acr, 0);
|
|
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs))
|
|
return;
|
|
|
|
_gdd.grfgdd = fgddFill;
|
|
_gdd.acrFore = acr;
|
|
|
|
_pgpt->DrawOval(&rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Frame an oval with a two color pattern.
|
|
***************************************************************************/
|
|
void GNV::FrameOvalApt(RC *prc, APT *papt, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertVarMem(papt);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs) || _gdd.dxpPen == 0 && _gdd.dypPen == 0)
|
|
return;
|
|
|
|
_gdd.grfgdd = fgddFrame | fgddPattern;
|
|
_gdd.apt = *papt;
|
|
_gdd.apt.MoveOrigin(_rcDst.xpLeft - _rcSrc.xpLeft, _rcDst.ypTop - _rcSrc.ypTop);
|
|
_gdd.acrFore = acrFore;
|
|
_gdd.acrBack = acrBack;
|
|
|
|
_pgpt->DrawOval(&rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Frame an oval with a color.
|
|
***************************************************************************/
|
|
void GNV::FrameOval(RC *prc, ACR acr)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertPo(&acr, 0);
|
|
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs) || _gdd.dxpPen == 0 && _gdd.dypPen == 0)
|
|
return;
|
|
|
|
_gdd.grfgdd = fgddFrame;
|
|
_gdd.acrFore = acr;
|
|
|
|
_pgpt->DrawOval(&rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw a line with a pattern. Sets the pen position to (xp2, yp2).
|
|
***************************************************************************/
|
|
void GNV::LineApt(long xp1, long yp1, long xp2, long yp2,
|
|
APT *papt, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(papt);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
PTS pts1, pts2;
|
|
|
|
if (_gdd.dxpPen != 0 || _gdd.dypPen != 0)
|
|
{
|
|
_gdd.grfgdd = fgddFrame | fgddPattern;
|
|
_gdd.apt = *papt;
|
|
_gdd.apt.MoveOrigin(_rcDst.xpLeft - _rcSrc.xpLeft, _rcDst.ypTop - _rcSrc.ypTop);
|
|
_gdd.acrFore = acrFore;
|
|
_gdd.acrBack = acrBack;
|
|
|
|
_MapPtPts(xp1, yp1, &pts1);
|
|
_MapPtPts(xp2, yp2, &pts2);
|
|
_pgpt->DrawLine(&pts1, &pts2, &_gdd);
|
|
}
|
|
_xp = xp2;
|
|
_yp = yp2;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw a line in a solid color. Sets the pen position to (xp2, yp2).
|
|
***************************************************************************/
|
|
void GNV::Line(long xp1, long yp1, long xp2, long yp2, ACR acr)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(&acr, 0);
|
|
PTS pts1, pts2;
|
|
|
|
if (_gdd.dxpPen != 0 || _gdd.dypPen != 0)
|
|
{
|
|
_gdd.grfgdd = fgddFrame;
|
|
_gdd.acrFore = acr;
|
|
|
|
_MapPtPts(xp1, yp1, &pts1);
|
|
_MapPtPts(xp2, yp2, &pts2);
|
|
_pgpt->DrawLine(&pts1, &pts2, &_gdd);
|
|
}
|
|
_xp = xp2;
|
|
_yp = yp2;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fill a polygon with a pattern.
|
|
***************************************************************************/
|
|
void GNV::FillOgnApt(POGN pogn, APT *papt, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pogn, 0);
|
|
AssertVarMem(papt);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
HQ hqoly;
|
|
|
|
if ((hqoly = _HqolyCreate(pogn, fognAutoClose)) == hqNil)
|
|
{
|
|
PushErc(ercGfxCantDraw);
|
|
return;
|
|
}
|
|
|
|
_gdd.grfgdd = fgddFill | fgddPattern;
|
|
_gdd.apt = *papt;
|
|
_gdd.apt.MoveOrigin(_rcDst.xpLeft - _rcSrc.xpLeft, _rcDst.ypTop - _rcSrc.ypTop);
|
|
_gdd.acrFore = acrFore;
|
|
_gdd.acrBack = acrBack;
|
|
|
|
_pgpt->DrawPoly(hqoly, &_gdd);
|
|
FreePhq(&hqoly);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fill a polygon with a color.
|
|
***************************************************************************/
|
|
void GNV::FillOgn(POGN pogn, ACR acr)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pogn, 0);
|
|
AssertPo(&acr, 0);
|
|
HQ hqoly;
|
|
|
|
if ((hqoly = _HqolyCreate(pogn, fognAutoClose)) == hqNil)
|
|
{
|
|
PushErc(ercGfxCantDraw);
|
|
return;
|
|
}
|
|
|
|
_gdd.grfgdd = fgddFill;
|
|
_gdd.acrFore = acr;
|
|
|
|
_pgpt->DrawPoly(hqoly, &_gdd);
|
|
FreePhq(&hqoly);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Frame a polygon with a pattern.
|
|
NOTE: Using kacrInvert produces slightly different results on the Mac.
|
|
(Mac only does alternate winding).
|
|
***************************************************************************/
|
|
void GNV::FrameOgnApt(POGN pogn, APT *papt, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pogn, 0);
|
|
AssertVarMem(papt);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
HQ hqoly;
|
|
|
|
if (_gdd.dxpPen == 0 && _gdd.dypPen == 0)
|
|
return;
|
|
|
|
if (hqNil == (hqoly = _HqolyFrame(pogn, fognAutoClose)))
|
|
{
|
|
PushErc(ercGfxCantDraw);
|
|
return;
|
|
}
|
|
|
|
_gdd.grfgdd = fgddFrame | fgddPattern;
|
|
_gdd.apt = *papt;
|
|
_gdd.apt.MoveOrigin(_rcDst.xpLeft - _rcSrc.xpLeft, _rcDst.ypTop - _rcSrc.ypTop);
|
|
_gdd.acrFore = acrFore;
|
|
_gdd.acrBack = acrBack;
|
|
|
|
_pgpt->DrawPoly(hqoly, &_gdd);
|
|
FreePhq(&hqoly);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Frame a polygon with a color.
|
|
NOTE: Using kacrInvert produces slightly different results on the Mac.
|
|
***************************************************************************/
|
|
void GNV::FrameOgn(POGN pogn, ACR acr)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pogn, 0);
|
|
AssertPo(&acr, 0);
|
|
HQ hqoly;
|
|
|
|
if (_gdd.dxpPen == 0 && _gdd.dypPen == 0)
|
|
return;
|
|
|
|
if (hqNil == (hqoly = _HqolyFrame(pogn, fognAutoClose)))
|
|
{
|
|
PushErc(ercGfxCantDraw);
|
|
return;
|
|
}
|
|
|
|
_gdd.grfgdd = fgddFrame;
|
|
_gdd.acrFore = acr;
|
|
|
|
_pgpt->DrawPoly(hqoly, &_gdd);
|
|
FreePhq(&hqoly);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Frame a poly-line with a pattern.
|
|
NOTE: Using kacrInvert produces slightly different results on the Mac.
|
|
***************************************************************************/
|
|
void GNV::FramePolyLineApt(POGN pogn, APT *papt, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pogn, 0);
|
|
AssertVarMem(papt);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
HQ hqoly;
|
|
|
|
if (_gdd.dxpPen == 0 && _gdd.dypPen == 0)
|
|
return;
|
|
|
|
if (hqNil == (hqoly = _HqolyFrame(pogn, fognNil)))
|
|
{
|
|
PushErc(ercGfxCantDraw);
|
|
return;
|
|
}
|
|
|
|
_gdd.grfgdd = fgddFrame | fgddPattern;
|
|
_gdd.apt = *papt;
|
|
_gdd.apt.MoveOrigin(_rcDst.xpLeft - _rcSrc.xpLeft, _rcDst.ypTop - _rcSrc.ypTop);
|
|
_gdd.acrFore = acrFore;
|
|
_gdd.acrBack = acrBack;
|
|
|
|
_pgpt->DrawPoly(hqoly, &_gdd);
|
|
FreePhq(&hqoly);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Frame a poly-line with a color.
|
|
NOTE: Using kacrInvert produces slightly different results on the Mac.
|
|
***************************************************************************/
|
|
void GNV::FramePolyLine(POGN pogn, ACR acr)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pogn, 0);
|
|
AssertPo(&acr, 0);
|
|
HQ hqoly;
|
|
|
|
if (_gdd.dxpPen == 0 && _gdd.dypPen == 0)
|
|
return;
|
|
|
|
if (hqNil == (hqoly = _HqolyCreate(pogn, fognNil)))
|
|
{
|
|
PushErc(ercGfxCantDraw);
|
|
return;
|
|
}
|
|
|
|
_gdd.grfgdd = fgddFrame;
|
|
_gdd.acrFore = acr;
|
|
|
|
_pgpt->DrawPoly(hqoly, &_gdd);
|
|
FreePhq(&hqoly);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Convert an OGN into a polygon record (hqoly). This maps the points and
|
|
optionally closes the polygon and/or calculates the bounds (Mac only).
|
|
***************************************************************************/
|
|
HQ GNV::_HqolyCreate(POGN pogn, ulong grfogn)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pogn, 0);
|
|
HQ hqoly;
|
|
long ipt;
|
|
long cb;
|
|
long cpt;
|
|
OLY *poly;
|
|
PT *ppt;
|
|
PTS *ppts;
|
|
|
|
if ((cpt = pogn->IvMac()) < 2)
|
|
return hqNil;
|
|
|
|
cb = kcbOlyBase + LwMul(cpt, size(PTS));
|
|
if (cpt < 3)
|
|
grfogn &= ~fognAutoClose;
|
|
else if (grfogn & fognAutoClose)
|
|
cb += size(PTS);
|
|
|
|
if (!FAllocHq(&hqoly, cb, fmemNil, mprNormal))
|
|
return hqNil;
|
|
|
|
poly = (OLY *)PvLockHq(hqoly);
|
|
#ifdef MAC
|
|
poly->cb = (short)cb;
|
|
Assert(cb == poly->cb, "polygon too big");
|
|
poly->rcs.left = kswMax;
|
|
poly->rcs.right = kswMin;
|
|
poly->rcs.top = kswMax;
|
|
poly->rcs.bottom = kswMin;
|
|
#else //!MAC
|
|
poly->cpts = cpt + FPure(grfogn & fognAutoClose);
|
|
#endif //!MAC
|
|
|
|
ppt = pogn->PrgptLock();
|
|
ppts = (PTS *)poly->rgpts;
|
|
for (ipt = 0; ipt < cpt; ipt++, ppt++, ppts++)
|
|
{
|
|
_MapPtPts(ppt->xp, ppt->yp, ppts);
|
|
#ifdef MAC
|
|
// Compute bounding rectangle.
|
|
if (poly->rcs.top > ppts->v)
|
|
poly->rcs.top = ppts->v;
|
|
if (poly->rcs.left > ppts->h)
|
|
poly->rcs.left = ppts->h;
|
|
if (poly->rcs.bottom < ppts->v)
|
|
poly->rcs.bottom = ppts->v;
|
|
if (poly->rcs.right < ppts->h)
|
|
poly->rcs.right = ppts->h;
|
|
#endif //MAC
|
|
}
|
|
if (grfogn & fognAutoClose)
|
|
*ppts = poly->rgpts[0];
|
|
|
|
pogn->Unlock();
|
|
UnlockHq(hqoly);
|
|
return hqoly;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Convert a polygon (OGN) into a polygon record (hqoly). On Windows,
|
|
this actually generates a new polygon that is the outline of the framed
|
|
path (which we'll tell GDI to fill). On the Mac, this just calls
|
|
_HqolyCreate.
|
|
***************************************************************************/
|
|
HQ GNV::_HqolyFrame(POGN pogn, ulong grfogn)
|
|
{
|
|
#ifdef WIN
|
|
AssertThis(0);
|
|
AssertPo(pogn, 0);
|
|
HQ hqoly;
|
|
|
|
POGN pognUse;
|
|
PT rgptPen[4]; // Pen rectangle vectors.
|
|
|
|
rgptPen[0].xp = 0;
|
|
rgptPen[0].yp = 0;
|
|
rgptPen[1].xp = _gdd.dxpPen;
|
|
rgptPen[1].yp = 0;
|
|
rgptPen[2].xp = _gdd.dxpPen;
|
|
rgptPen[2].yp = _gdd.dypPen;
|
|
rgptPen[3].xp = 0;
|
|
rgptPen[3].yp = _gdd.dypPen;
|
|
|
|
if (pvNil == (pognUse = pogn->PognTraceRgpt(rgptPen, 4, grfogn)))
|
|
return pvNil;
|
|
hqoly = _HqolyCreate(pognUse, grfogn);
|
|
ReleasePpo(&pognUse);
|
|
|
|
return hqoly;
|
|
#elif defined(MAC)
|
|
return _HqolyCreate(pogn, grfogn);
|
|
#endif //MAC
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Scroll the given rectangle by (dxp, dyp). If prc1 is not nil fill it
|
|
with the first uncovered rectangle. If prc2 is not nil fill it
|
|
with the second uncovered rectangle (if there is one).
|
|
***************************************************************************/
|
|
void GNV::ScrollRc(RC *prc, long dxp, long dyp, RC *prc1, RC *prc2)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertNilOrVarMem(prc1);
|
|
AssertNilOrVarMem(prc2);
|
|
PTS pts;
|
|
RCS rcs;
|
|
PT pt;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs) || dxp == 0 && dyp == 0)
|
|
{
|
|
if (pvNil != prc1)
|
|
prc1->Zero();
|
|
if (pvNil != prc2)
|
|
prc2->Zero();
|
|
return;
|
|
}
|
|
|
|
_MapPtPts(prc->xpLeft + dxp, prc->ypTop + dyp, &pts);
|
|
pt = pts;
|
|
_pgpt->ScrollRcs(&rcs, pt.xp - rcs.left, pt.yp - rcs.top, &_gdd);
|
|
|
|
GetBadRcForScroll(prc, dxp, dyp, prc1, prc2);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to get the RC's that are uncovered during a scroll
|
|
operation.
|
|
***************************************************************************/
|
|
void GNV::GetBadRcForScroll(RC *prc, long dxp, long dyp, RC *prc1, RC *prc2)
|
|
{
|
|
AssertNilOrVarMem(prc1);
|
|
AssertNilOrVarMem(prc2);
|
|
|
|
if (pvNil != prc1)
|
|
{
|
|
*prc1 = *prc;
|
|
if (0 < dxp)
|
|
prc1->xpRight = prc1->xpLeft + dxp;
|
|
else if (0 > dxp)
|
|
prc1->xpLeft = prc1->xpRight + dxp;
|
|
else if (0 < dyp)
|
|
prc1->ypBottom = prc1->ypTop + dyp;
|
|
else
|
|
prc1->ypTop = prc1->ypBottom + dyp;
|
|
prc1->FIntersect(prc);
|
|
}
|
|
|
|
if (pvNil != prc2)
|
|
{
|
|
if (0 == dxp || 0 == dyp)
|
|
prc2->Zero();
|
|
else
|
|
{
|
|
*prc2 = *prc;
|
|
if (0 < dyp)
|
|
prc2->ypBottom = prc2->ypTop + dyp;
|
|
else
|
|
prc2->ypTop = prc2->ypBottom + dyp;
|
|
if (0 < dxp)
|
|
prc2->xpLeft += dxp;
|
|
else
|
|
prc2->xpRight += dxp;
|
|
prc2->FIntersect(prc);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the source rectangle.
|
|
***************************************************************************/
|
|
void GNV::GetRcSrc(RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
*prc = _rcSrc;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the source rectangle.
|
|
***************************************************************************/
|
|
void GNV::SetRcSrc(RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
|
|
_rcSrc = *prc;
|
|
if (_rcSrc.FEmpty())
|
|
{
|
|
_rcSrc.xpRight = _rcSrc.xpLeft + 1;
|
|
_rcSrc.ypBottom = _rcSrc.ypTop + 1;
|
|
}
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the destination rectangle.
|
|
***************************************************************************/
|
|
void GNV::GetRcDst(RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
PT pt;
|
|
|
|
*prc = _rcDst;
|
|
_pgpt->GetPtBase(&pt);
|
|
prc->Offset(pt.xp, pt.yp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the destination rectangle. Also opens up the vis rc and clipping
|
|
and sets default font and pen values.
|
|
***************************************************************************/
|
|
void GNV::SetRcDst(RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
PT pt;
|
|
|
|
_rcDst = *prc;
|
|
if (_rcDst.FEmpty())
|
|
{
|
|
_rcDst.xpRight = _rcDst.xpLeft + 1;
|
|
_rcDst.ypBottom = _rcDst.ypTop + 1;
|
|
}
|
|
_pgpt->GetPtBase(&pt);
|
|
_rcDst.Offset(-pt.xp, -pt.yp);
|
|
_gdd.prcsClip = pvNil;
|
|
_rcVis.Max();
|
|
|
|
_dsf.onn = vntl.OnnSystem();
|
|
_dsf.grfont = fontNil;
|
|
_dsf.dyp = vpappb->DypTextDef();
|
|
_dsf.tah = tahLeft;
|
|
_dsf.tav = tavTop;
|
|
_gdd.dxpPen = _gdd.dypPen = 1;
|
|
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the visible rectangle. When ClipRc is called, the rc is first
|
|
intersected with the vis rc. Passing pvNil opens up the vis rectangle
|
|
(so there is no natural clipping). This should be called _after_
|
|
SetRcDst, since SetRcDst opens up the vis rc. This also opens the
|
|
clipping to the vis rc.
|
|
***************************************************************************/
|
|
void GNV::SetRcVis(RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(prc);
|
|
|
|
if (prc == pvNil)
|
|
{
|
|
_rcVis.Max();
|
|
_gdd.prcsClip = pvNil;
|
|
}
|
|
else
|
|
{
|
|
_rcVis = *prc;
|
|
_rcVis.Map(&_rcSrc, &_rcDst);
|
|
_rcsClip = RCS(_rcVis);
|
|
_gdd.prcsClip = &_rcsClip;
|
|
}
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Intersect the current vis rectangle with the given rectangle and make
|
|
that the new vis rectangle. Opens the clipping to the vis rectangle
|
|
also.
|
|
***************************************************************************/
|
|
void GNV::IntersectRcVis(RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
RC rc;
|
|
|
|
rc = *prc;
|
|
rc.Map(&_rcSrc, &_rcDst);
|
|
_rcVis.FIntersect(&rc);
|
|
_rcsClip = RCS(_rcVis);
|
|
_gdd.prcsClip = &_rcsClip;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the clipping (in source coordinates). If prc is pvNil, opens up
|
|
the clipping (to the vis rectangle). Otherwise, sets the clipping
|
|
to the intersection of the vis rectangle and *prc.
|
|
***************************************************************************/
|
|
void GNV::ClipRc(RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(prc);
|
|
|
|
RC rc;
|
|
|
|
if (prc == pvNil)
|
|
{
|
|
rc.Max();
|
|
if (rc == _rcVis)
|
|
{
|
|
//no clipping
|
|
_gdd.prcsClip = pvNil;
|
|
return;
|
|
}
|
|
rc = _rcVis;
|
|
}
|
|
else
|
|
{
|
|
rc = *prc;
|
|
rc.Map(&_rcSrc, &_rcDst);
|
|
rc.FIntersect(&_rcVis);
|
|
}
|
|
|
|
_rcsClip = RCS(rc);
|
|
_gdd.prcsClip = &_rcsClip;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Clip to the source rectangle.
|
|
***************************************************************************/
|
|
void GNV::ClipToSrc(void)
|
|
{
|
|
AssertThis(0);
|
|
ClipRc(&_rcSrc);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the pen size (in source coordinates).
|
|
***************************************************************************/
|
|
void GNV::SetPenSize(long dxpPen, long dypPen)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(dxpPen, 0, kswMax);
|
|
AssertIn(dypPen, 0, kswMax);
|
|
_gdd.dxpPen = LwMulDivAway(dxpPen, _rcDst.Dxp(), _rcSrc.Dxp());
|
|
_gdd.dypPen = LwMulDivAway(dypPen, _rcDst.Dyp(), _rcSrc.Dyp());
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the current font info.
|
|
***************************************************************************/
|
|
void GNV::SetFont(long onn, ulong grfont, long dypFont, long tah, long tav)
|
|
{
|
|
AssertThis(0);
|
|
_dsf.onn = onn;
|
|
_dsf.grfont = grfont;
|
|
_dsf.dyp = LwMulDivAway(dypFont, _rcDst.Dyp(), _rcSrc.Dyp());
|
|
_dsf.tah = tah;
|
|
_dsf.tav = tav;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the current font.
|
|
***************************************************************************/
|
|
void GNV::SetOnn(long onn)
|
|
{
|
|
AssertThis(0);
|
|
_dsf.onn = onn;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the current font style.
|
|
***************************************************************************/
|
|
void GNV::SetFontStyle(ulong grfont)
|
|
{
|
|
AssertThis(0);
|
|
_dsf.grfont = grfont;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the current font size.
|
|
***************************************************************************/
|
|
void GNV::SetFontSize(long dyp)
|
|
{
|
|
AssertThis(0);
|
|
_dsf.dyp = LwMulDivAway(dyp, _rcDst.Dyp(), _rcSrc.Dyp());
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the current font alignment.
|
|
***************************************************************************/
|
|
void GNV::SetFontAlign(long tah, long tav)
|
|
{
|
|
AssertThis(0);
|
|
_dsf.tah = tah;
|
|
_dsf.tav = tav;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Set the current font. Font size must be specified in Dst units.
|
|
******************************************************************************/
|
|
void GNV::SetDsf(DSF *pdsf)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pdsf, 0);
|
|
|
|
_dsf = *pdsf;
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Get the current font. Font size is specified in Dst units.
|
|
******************************************************************************/
|
|
void GNV::GetDsf(DSF *pdsf)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pdsf);
|
|
*pdsf = _dsf;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Draw some text.
|
|
******************************************************************************/
|
|
void GNV::DrawRgch(achar *prgch, long cch, long xp, long yp, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cch, 0, kcbMax);
|
|
AssertPvCb(prgch, cch);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
|
|
PTS pts;
|
|
|
|
if (cch == 0)
|
|
return;
|
|
|
|
_gdd.acrFore = acrFore;
|
|
_gdd.acrBack = acrBack;
|
|
_MapPtPts(xp, yp, &pts);
|
|
_pgpt->DrawRgch(prgch, cch, pts, &_gdd, &_dsf);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the given string.
|
|
***************************************************************************/
|
|
void GNV::DrawStn(PSTN pstn, long xp, long yp, ACR acrFore, ACR acrBack)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstn, 0);
|
|
AssertPo(&acrFore, 0);
|
|
AssertPo(&acrBack, 0);
|
|
|
|
DrawRgch(pstn->Prgch(), pstn->Cch(), xp, yp, acrFore, acrBack);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Return the bounding box of the text. If the GNV has any scaling, this
|
|
is approximate. This even works if cch is 0 (just gives the height).
|
|
******************************************************************************/
|
|
void GNV::GetRcFromRgch(RC *prc, achar *prgch, long cch, long xp, long yp)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertIn(cch, 0, kcbMax);
|
|
AssertPvCb(prgch, cch);
|
|
|
|
PTS pts;
|
|
RCS rcs;
|
|
|
|
_MapPtPts(xp, yp, &pts);
|
|
_pgpt->GetRcsFromRgch(&rcs, prgch, cch, pts, &_dsf);
|
|
*prc = rcs;
|
|
prc->Map(&_rcDst, &_rcSrc);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Return the bounding box of the text. If the GNV has any scaling, this
|
|
is approximate. This even works if the string is empty (gives the height).
|
|
******************************************************************************/
|
|
void GNV::GetRcFromStn(RC *prc, PSTN pstn, long xp, long yp)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertPo(pstn, 0);
|
|
|
|
GetRcFromRgch(prc, pstn->Prgch(), pstn->Cch(), xp, yp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Copy bits from a GNV to this one.
|
|
***************************************************************************/
|
|
void GNV::CopyPixels(PGNV pgnvSrc, RC *prcSrc, RC *prcDst)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnvSrc, 0);
|
|
AssertVarMem(prcSrc);
|
|
AssertVarMem(prcDst);
|
|
RCS rcsSrc, rcsDst;
|
|
|
|
if (!pgnvSrc->_FMapRcRcs(prcSrc, &rcsSrc) || !_FMapRcRcs(prcDst, &rcsDst))
|
|
return;
|
|
_pgpt->CopyPixels(pgnvSrc->_pgpt, &rcsSrc, &rcsDst, &_gdd);
|
|
}
|
|
|
|
|
|
ulong _mpgfdgrfpt[4] =
|
|
{
|
|
fptNegateXp,
|
|
fptNil,
|
|
fptNegateYp | fptTranspose,
|
|
fptTranspose
|
|
};
|
|
|
|
|
|
ulong _mpgfdgrfptInv[4] =
|
|
{
|
|
fptNegateXp,
|
|
fptNil,
|
|
fptNegateXp | fptTranspose,
|
|
fptTranspose
|
|
};
|
|
|
|
|
|
/***************************************************************************
|
|
Get the old palette in *ppglclrOld, allocate a transitionary palette
|
|
in *ppglclrTrans, and get init the palette animation. On failure set
|
|
the new palette and set *ppglclrOld and *ppglclrTrans to nil.
|
|
If cbitPixel is not zero and not the depth of this device, this sets
|
|
the palette and returns false.
|
|
***************************************************************************/
|
|
bool GNV::_FInitPaletteTrans(PGL pglclr, PGL *ppglclrOld, PGL *ppglclrTrans,
|
|
long cbitPixel)
|
|
{
|
|
AssertNilOrPo(pglclr, 0);
|
|
AssertVarMem(ppglclrOld);
|
|
AssertVarMem(ppglclrTrans);
|
|
long cclr = pvNil == pglclr ? 256 : pglclr->IvMac();
|
|
|
|
*ppglclrOld = pvNil;
|
|
*ppglclrTrans = pvNil;
|
|
|
|
// get the current palette and set up the temporary transitionary palette
|
|
if (0 != cbitPixel && _pgpt->CbitPixel() != cbitPixel ||
|
|
pvNil == (*ppglclrOld = GPT::PglclrGetPalette()) ||
|
|
0 == (cclr = LwMin((*ppglclrOld)->IvMac(), cclr)) ||
|
|
pvNil == (*ppglclrTrans = GL::PglNew(size(CLR), cclr)))
|
|
{
|
|
ReleasePpo(ppglclrOld);
|
|
if (pvNil != pglclr)
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
return fFalse;
|
|
}
|
|
|
|
AssertDo((*ppglclrTrans)->FSetIvMac(cclr), 0);
|
|
GPT::SetActiveColors(*ppglclrOld, fpalIdentity | fpalInitAnim);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Transition the palette. Merge pglclrOld and pglclrNew into pglclrTrans
|
|
and animate the palette to pglclrTrans. If either source palette is nil,
|
|
*pclrSub is used in place of the nil palette. acrSub must be an RGB color.
|
|
***************************************************************************/
|
|
void GNV::_PaletteTrans(PGL pglclrOld, PGL pglclrNew, long lwNum, long lwDen,
|
|
PGL pglclrTrans, CLR *pclrSub)
|
|
{
|
|
AssertNilOrPo(pglclrOld, 0);
|
|
AssertNilOrPo(pglclrNew, 0);
|
|
AssertIn(lwDen, 1, kcbMax);
|
|
AssertIn(lwNum, 0, lwDen + 1);
|
|
AssertNilOrVarMem(pclrSub);
|
|
|
|
long iclr;
|
|
CLR clrOld, clrNew;
|
|
CLR clrSub;
|
|
|
|
iclr = pglclrTrans->IvMac();
|
|
if (pvNil != pglclrOld)
|
|
iclr = LwMin(pglclrOld->IvMac(), iclr);
|
|
if (pvNil != pglclrNew)
|
|
iclr = LwMin(pglclrNew->IvMac(), iclr);
|
|
if (pvNil != pclrSub)
|
|
clrSub = *pclrSub;
|
|
else
|
|
ClearPb(&clrSub, size(clrSub));
|
|
|
|
while (iclr-- > 0)
|
|
{
|
|
clrOld = clrNew = clrSub;
|
|
if (pvNil != pglclrOld)
|
|
pglclrOld->Get(iclr, &clrOld);
|
|
if (pvNil != pglclrNew)
|
|
pglclrNew->Get(iclr, &clrNew);
|
|
|
|
clrOld.bRed += (byte)LwMulDiv((long)clrNew.bRed - clrOld.bRed,
|
|
lwNum, lwDen);
|
|
clrOld.bGreen += (byte)LwMulDiv((long)clrNew.bGreen - clrOld.bGreen,
|
|
lwNum, lwDen);
|
|
clrOld.bBlue += (byte)LwMulDiv((long)clrNew.bBlue - clrOld.bBlue,
|
|
lwNum, lwDen);
|
|
pglclrTrans->Put(iclr, &clrOld);
|
|
}
|
|
|
|
GPT::SetActiveColors(pglclrTrans, fpalIdentity | fpalAnimate);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a temporary GNV that is a copy of the given rectangle in this
|
|
GNV. This is used for several transitions.
|
|
***************************************************************************/
|
|
bool GNV::_FEnsureTempGnv(PGNV *ppgnv, RC *prc)
|
|
{
|
|
PGPT pgpt;
|
|
PGNV pgnv;
|
|
|
|
if (pvNil == (pgpt = GPT::PgptNewOffscreen(prc, 8)) ||
|
|
pvNil == (pgnv = NewObj GNV(pgpt)))
|
|
{
|
|
ReleasePpo(&pgpt);
|
|
*ppgnv = pvNil;
|
|
return fFalse;
|
|
}
|
|
|
|
ReleasePpo(&pgpt);
|
|
pgnv->CopyPixels(this, prc, prc);
|
|
GPT::Flush();
|
|
*ppgnv = pgnv;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Wipe the source gnv onto this one. If acrFill is not kacrClear, first
|
|
wipe acrFill on. The source and destination rectangles must be the same
|
|
size. gfd indicates which direction the wipe is. If pglclr is not
|
|
nil and acrFill is clear, the palette transition is gradual.
|
|
***************************************************************************/
|
|
void GNV::Wipe(long gfd, ACR acrFill, PGNV pgnvSrc, RC *prcSrc, RC *prcDst,
|
|
ulong dts, PGL pglclr)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(&acrFill, 0);
|
|
AssertPo(pgnvSrc, 0);
|
|
AssertVarMem(prcSrc);
|
|
AssertVarMem(prcDst);
|
|
AssertNilOrPo(pglclr, 0);
|
|
|
|
ulong grfpt, grfptInv;
|
|
ulong tsStart, dtsT;
|
|
long dxp, dxpTot;
|
|
long cact;
|
|
RC rcSrc, rcDst;
|
|
RC rc1, rc2;
|
|
PGL pglclrOld = pvNil;
|
|
PGL pglclrTrans = pvNil;
|
|
|
|
Assert(prcSrc->Dyp() == prcDst->Dyp() && prcSrc->Dxp() == prcDst->Dxp(),
|
|
"rc's are scaled");
|
|
|
|
GPT::Flush();
|
|
if (!FIn(dts, 1, kdtsMaxTrans))
|
|
dts = kdtsSecond;
|
|
|
|
for (cact = 0; cact < 2; cact++)
|
|
{
|
|
grfpt = _mpgfdgrfpt[gfd & 0x03];
|
|
grfptInv = _mpgfdgrfptInv[gfd & 0x03];
|
|
gfd >>= 2;
|
|
|
|
if (cact == 0)
|
|
{
|
|
if (kacrClear == acrFill)
|
|
continue;
|
|
}
|
|
else if (pvNil != pglclr &&
|
|
!_FInitPaletteTrans(pglclr, &pglclrOld, &pglclrTrans, 8))
|
|
{
|
|
pglclr = pvNil; // so we don't try to transition
|
|
}
|
|
|
|
tsStart = TsCurrent();
|
|
rcSrc = *prcSrc;
|
|
rcDst = *prcDst;
|
|
rcSrc.Transform(grfpt);
|
|
rcDst.Transform(grfpt);
|
|
dxpTot = rcDst.Dxp();
|
|
for (dxp = 0; dxp < dxpTot; )
|
|
{
|
|
rc1 = rcSrc;
|
|
rc2 = rcDst;
|
|
rc1.xpLeft = rcSrc.xpLeft + dxp;
|
|
rc2.xpLeft = rcDst.xpLeft + dxp;
|
|
dtsT = LwMin(TsCurrent() - tsStart, dts);
|
|
dxp = LwMulDiv(dxpTot, dtsT, dts);
|
|
rc1.xpRight = rcSrc.xpLeft + dxp;
|
|
rc2.xpRight = rcDst.xpLeft + dxp;
|
|
|
|
if (cact == 1 && pglclr != pvNil)
|
|
_PaletteTrans(pglclrOld, pglclr, dtsT, dts, pglclrTrans);
|
|
|
|
if (!rc2.FEmpty())
|
|
{
|
|
rc1.Transform(grfptInv);
|
|
rc2.Transform(grfptInv);
|
|
if (cact == 0)
|
|
FillRc(&rc2, acrFill);
|
|
else
|
|
CopyPixels(pgnvSrc, &rc1, &rc2);
|
|
GPT::Flush();
|
|
}
|
|
}
|
|
|
|
if (pvNil != pglclr)
|
|
{
|
|
// set the palette
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
pglclr = pvNil; // so we don't transition during the second wipe
|
|
}
|
|
}
|
|
|
|
ReleasePpo(&pglclrOld);
|
|
ReleasePpo(&pglclrTrans);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Slide the source gnv onto this one. The source and destination
|
|
rectangles must be the same size.
|
|
***************************************************************************/
|
|
void GNV::Slide(long gfd, ACR acrFill, PGNV pgnvSrc, RC *prcSrc, RC *prcDst,
|
|
ulong dts, PGL pglclr)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(&acrFill, 0);
|
|
AssertPo(pgnvSrc, 0);
|
|
AssertVarMem(prcSrc);
|
|
AssertVarMem(prcDst);
|
|
AssertNilOrPo(pglclr, 0);
|
|
|
|
ulong grfpt, grfptInv;
|
|
ulong dtsT, tsStart;
|
|
long cact;
|
|
long dxp, dxpTot, dxpOld;
|
|
RC rcSrc, rcDst;
|
|
RC rc1, rc2;
|
|
PGNV pgnv;
|
|
PT dpt;
|
|
PGL pglclrOld = pvNil;
|
|
PGL pglclrTrans = pvNil;
|
|
|
|
Assert(prcSrc->Dyp() == prcDst->Dyp() && prcSrc->Dxp() == prcDst->Dxp(),
|
|
"rc's are scaled");
|
|
|
|
// allocate the offscreen port and copy the destination into it.
|
|
if (!_FEnsureTempGnv(&pgnv, prcDst))
|
|
{
|
|
if (pvNil != pglclr)
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
CopyPixels(pgnvSrc, prcSrc, prcDst);
|
|
GPT::Flush();
|
|
return;
|
|
}
|
|
|
|
if (!FIn(dts, 1, kdtsMaxTrans))
|
|
dts = kdtsSecond;
|
|
|
|
for (cact = 0; cact < 2; cact++)
|
|
{
|
|
grfpt = _mpgfdgrfpt[gfd & 0x03];
|
|
grfptInv = _mpgfdgrfptInv[gfd & 0x03];
|
|
gfd >>= 2;
|
|
|
|
if (cact == 0)
|
|
{
|
|
if (kacrClear == acrFill)
|
|
continue;
|
|
}
|
|
else if (pvNil != pglclr &&
|
|
!_FInitPaletteTrans(pglclr, &pglclrOld, &pglclrTrans))
|
|
{
|
|
pglclr = pvNil; // so we don't try to transition
|
|
}
|
|
|
|
tsStart = TsCurrent();
|
|
rcSrc = *prcSrc;
|
|
rcDst = *prcDst;
|
|
rcSrc.Transform(grfpt);
|
|
rcDst.Transform(grfpt);
|
|
dxpTot = rcDst.Dxp();
|
|
for (dxp = 0; dxp < dxpTot; )
|
|
{
|
|
dxpOld = dxp;
|
|
dtsT = LwMin(TsCurrent() - tsStart, dts);
|
|
dxp = LwMulDiv(dxpTot, dtsT, dts);
|
|
|
|
if (dxp != dxpOld)
|
|
{
|
|
// scroll the stuff that's already there
|
|
dpt.xp = dxp - dxpOld;
|
|
dpt.yp = 0;
|
|
dpt.Transform(grfptInv);
|
|
pgnv->ScrollRc(prcDst, dpt.xp, dpt.yp);
|
|
|
|
// copy in the new stuff
|
|
rc1 = rcSrc;
|
|
rc2 = rcDst;
|
|
rc1.xpLeft = rc1.xpRight - dxp;
|
|
rc1.xpRight -= dxpOld;
|
|
rc2.xpRight = rc2.xpLeft + dxp - dxpOld;
|
|
rc1.Transform(grfptInv);
|
|
rc2.Transform(grfptInv);
|
|
if (cact == 0)
|
|
pgnv->FillRc(&rc2, acrFill);
|
|
else
|
|
pgnv->CopyPixels(pgnvSrc, &rc1, &rc2);
|
|
}
|
|
|
|
if (pvNil != pglclr && 1 == cact)
|
|
_PaletteTrans(pglclrOld, pglclr, dtsT, dts, pglclrTrans);
|
|
|
|
if (dxp != dxpOld)
|
|
{
|
|
// copy the result to the destination
|
|
CopyPixels(pgnv, prcDst, prcDst);
|
|
GPT::Flush();
|
|
}
|
|
}
|
|
|
|
if (pvNil != pglclr)
|
|
{
|
|
// set the palette
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
|
|
// if we're not in 8 bit and cact is 1, copy the pixels so we
|
|
// make sure we've drawn the picture after the last palette change
|
|
if (1 == cact && _pgpt->CbitPixel() != 8)
|
|
{
|
|
CopyPixels(pgnv, prcDst, prcDst);
|
|
GPT::Flush();
|
|
}
|
|
pglclr = pvNil; // so we don't transition during the second wipe
|
|
}
|
|
}
|
|
|
|
ReleasePpo(&pgnv);
|
|
ReleasePpo(&pglclrOld);
|
|
ReleasePpo(&pglclrTrans);
|
|
}
|
|
|
|
|
|
|
|
// klwPrime must be a prime and klwRoot must be a primitive root for klwPrime.
|
|
// the code under #ifdef SPECIAL_PRIME below assumes that klwPrime is
|
|
// (2^16 + 1) and klwRoot is (2 ^15 - 1). If you change klwPrime and/or
|
|
// klwRoot, make sure that SPECIAL_PRIME is not defined.
|
|
|
|
#define SPECIAL_PRIME
|
|
#define klwPrime 65537 // a prime
|
|
#define klwRoot 32767 // a primitive root for the prime
|
|
|
|
|
|
/***************************************************************************
|
|
Returns the next quasi-random number for Dissolve.
|
|
***************************************************************************/
|
|
inline long _LwNextDissolve(long lw)
|
|
{
|
|
AssertIn(lw, 1, klwPrime);
|
|
|
|
#ifdef SPECIAL_PRIME
|
|
|
|
Assert(klwPrime == 0x00010001, 0);
|
|
Assert(klwRoot == 0x00007FFF, 0);
|
|
|
|
// multiply by 2^15 - 1
|
|
lw = (lw << 15) - lw;
|
|
|
|
// mod by 2^16 + 1
|
|
lw = (lw & 0x0000FFFFL) - (long)((ulong)lw >> 16);
|
|
if (lw < 0)
|
|
lw += klwPrime;
|
|
|
|
#else //!SPECIAL_PRIME
|
|
|
|
LwMulDivMod(lw, klwRoot, klwPrime, &lw);
|
|
|
|
#endif //!SPECIAL_PRIME
|
|
|
|
return lw;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Dissolve the source gnv into this one. If acrFill is not kacrClear,
|
|
first dissolve into a solid acrFill, then into the source. The source
|
|
and destination rectangles must be the same size. If pgnvSrc is nil,
|
|
just dissolve into the solid color. Each portion is done in dts time.
|
|
***************************************************************************/
|
|
void GNV::Dissolve(long crcWidth, long crcHeight, ACR acrFill,
|
|
PGNV pgnvSrc, RC *prcSrc, RC *prcDst, ulong dts, PGL pglclr)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(&acrFill, 0);
|
|
AssertNilOrPo(pgnvSrc, 0);
|
|
AssertNilOrVarMem(prcSrc);
|
|
AssertVarMem(prcDst);
|
|
AssertNilOrPo(pglclr, 0);
|
|
AssertIn(crcWidth, 0, kcbMax);
|
|
AssertIn(crcHeight, 0, kcbMax);
|
|
|
|
ulong tsStart, dtsT;
|
|
byte bFill;
|
|
long cbRowSrc, cbRowDst;
|
|
RND rnd;
|
|
long lw, cact, irc, crc, crcFill, crcT;
|
|
RC rc1, rc2;
|
|
bool fOnScreen;
|
|
long dibExtra, dibRow, ibExtra;
|
|
byte *pbRow;
|
|
byte *prgbDst = pvNil;
|
|
byte *prgbSrc = pvNil;
|
|
PGNV pgnv = pvNil;
|
|
PGL pglclrOld = pvNil;
|
|
PGL pglclrTrans = pvNil;
|
|
|
|
if (prcDst->FEmpty())
|
|
return;
|
|
|
|
if (crcWidth <= 0 || crcHeight <= 0)
|
|
{
|
|
// do off screen pixel level dissolve
|
|
fOnScreen = fFalse;
|
|
|
|
// allocate the offscreen port and copy the destination into it.
|
|
if (pgnvSrc != pvNil)
|
|
{
|
|
PGPT pgptSrc;
|
|
|
|
AssertVarMem(prcSrc);
|
|
Assert(prcSrc->Dyp() == prcDst->Dyp() && prcSrc->Dxp() == prcDst->Dxp(),
|
|
"rc's are scaled");
|
|
pgptSrc = pgnvSrc->Pgpt();
|
|
if (pgptSrc->CbitPixel() != 8 ||
|
|
pvNil == (prgbSrc = pgptSrc->PrgbLockPixels(&rc2)))
|
|
{
|
|
Bug("Can't dissolve from this GPT");
|
|
goto LFail;
|
|
}
|
|
rc1 = *prcSrc;
|
|
rc1.Map(&pgnvSrc->_rcSrc, &pgnvSrc->_rcDst);
|
|
if (!rc2.FContains(&rc1))
|
|
{
|
|
Bug("Source rectangle is outside the source bitmap");
|
|
goto LFail;
|
|
}
|
|
cbRowSrc = pgptSrc->CbRow();
|
|
prgbSrc += LwMul(rc1.ypTop - rc2.ypTop, cbRowSrc) +
|
|
rc1.xpLeft - rc2.xpLeft;
|
|
}
|
|
|
|
if (!_FEnsureTempGnv(&pgnv, prcDst))
|
|
{
|
|
LFail:
|
|
if (pvNil != prgbSrc && pvNil != pgnvSrc)
|
|
pgnvSrc->Pgpt()->Unlock();
|
|
|
|
if (pvNil != pglclr)
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
if (pvNil != pgnvSrc)
|
|
CopyPixels(pgnvSrc, prcSrc, prcDst);
|
|
else
|
|
FillRc(prcDst, acrFill);
|
|
GPT::Flush();
|
|
return;
|
|
}
|
|
prgbDst = pgnv->Pgpt()->PrgbLockPixels();
|
|
cbRowDst = pgnv->Pgpt()->CbRow();
|
|
|
|
if (acrFill != kacrClear)
|
|
{
|
|
// get the byte value to fill with
|
|
byte bT = prgbDst[0];
|
|
|
|
rc1.Set(prcDst->xpLeft, prcDst->ypTop,
|
|
prcDst->xpLeft + 1, prcDst->ypTop + 1);
|
|
pgnv->FillRc(&rc1, acrFill);
|
|
GPT::Flush();
|
|
bFill = prgbDst[0];
|
|
prgbDst[0] = bT;
|
|
}
|
|
|
|
crcWidth = cbRowDst;
|
|
crcHeight = prcDst->Dyp();
|
|
crcFill = LwMul(crcWidth, crcHeight) + prcDst->Dxp() - cbRowDst;
|
|
|
|
dibExtra = (klwPrime - 1) % crcWidth;
|
|
dibRow = ((klwPrime - 1) / crcWidth) * cbRowSrc;
|
|
}
|
|
else
|
|
{
|
|
// on screen dissolve
|
|
fOnScreen = fTrue;
|
|
crcWidth = LwMin(prcDst->Dxp(), crcWidth);
|
|
crcHeight = LwMin(prcDst->Dyp(), crcHeight);
|
|
crcFill = LwMul(crcWidth, crcHeight);
|
|
}
|
|
|
|
if (!FIn(dts, 1, kdtsMaxTrans))
|
|
dts = kdtsSecond;
|
|
|
|
// the first time through, dissolve to the color; the second time
|
|
// through dissolve to the source bitmap
|
|
for (cact = 0; cact < 2; cact++)
|
|
{
|
|
if (cact == 0)
|
|
{
|
|
if (kacrClear == acrFill)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (pvNil == pgnvSrc)
|
|
goto LSetPalette;
|
|
if (pvNil != pglclr && !_FInitPaletteTrans(pglclr, &pglclrOld,
|
|
&pglclrTrans, fOnScreen ? 8 : 0))
|
|
{
|
|
pglclr = pvNil; // so we don't try to transition
|
|
}
|
|
}
|
|
|
|
// We start with lw a random value between 1 and (klwPrime - 1)
|
|
// (inclusive). Subsequent values of lw are computed as
|
|
// lw = lw * klwRoot % klwPrime. Because klwRoot is a primitive root
|
|
// of unity for klwPrime, lw will take on all values from 1 thru
|
|
// (klwPrime - 1) in seemingly random order.
|
|
|
|
irc = rnd.LwNext(crcFill);
|
|
lw = (crcFill - irc - 1) % (klwPrime - 1) + 1;
|
|
|
|
if (!fOnScreen && cact != 0)
|
|
{
|
|
// pbRow points to the row of prgbSrc that the current source
|
|
// pixel is in. ibExtra is the offset of the source pixel
|
|
// into the row (the "xp" coordinate of the source pixel).
|
|
// dibRow and dibExtra are for finding the source pixel
|
|
// incrementally. When we subtract (klwPrime - 1) from irc,
|
|
// we subtract dibRow from pbRow and dibExtra from ibExtra.
|
|
// If ibExtra goes negative, we add cbRowSrc to pbRow and
|
|
// crcWidth to ibExtra.
|
|
|
|
ibExtra = irc % crcWidth;
|
|
pbRow = prgbSrc + (irc / crcWidth) * cbRowSrc;
|
|
}
|
|
|
|
tsStart = TsCurrent();
|
|
for (crc = 0; crc < crcFill; )
|
|
{
|
|
dtsT = LwMin(TsCurrent() - tsStart, dts);
|
|
crcT = LwMulDiv(crcFill, dtsT, dts) - crc;
|
|
if (crcT <= 0)
|
|
goto LPaletteTrans;
|
|
crc += crcT;
|
|
|
|
if (fOnScreen)
|
|
{
|
|
for ( ; crcT > 0; crcT--)
|
|
{
|
|
// find the next rectangle to fill
|
|
for (irc -= klwPrime - 1; irc < 0; irc = crcFill - lw)
|
|
lw = _LwNextDissolve(lw);
|
|
|
|
rc1.SetToCell(prcDst, crcWidth, crcHeight, irc % crcWidth,
|
|
irc / crcWidth);
|
|
if (cact == 0)
|
|
FillRc(&rc1, acrFill);
|
|
else
|
|
{
|
|
rc2 = rc1;
|
|
rc2.Map(prcDst, prcSrc);
|
|
CopyPixels(pgnvSrc, &rc2, &rc1);
|
|
}
|
|
}
|
|
goto LPaletteTrans;
|
|
}
|
|
|
|
if (cact == 0)
|
|
{
|
|
// fill with bFill
|
|
for ( ; crcT > 0; crcT--)
|
|
{
|
|
// find the next pixel to fill
|
|
for (irc -= klwPrime - 1; irc < 0; irc = crcFill - lw)
|
|
lw = _LwNextDissolve(lw);
|
|
prgbDst[irc] = bFill;
|
|
}
|
|
goto LBlastToScreen;
|
|
}
|
|
|
|
// cact == 1, fill from prgbSrc
|
|
for ( ; crcT > 0; crcT--)
|
|
{
|
|
// find the next rectangle to fill
|
|
irc -= klwPrime - 1;
|
|
if (irc >= 0)
|
|
{
|
|
pbRow -= dibRow;
|
|
ibExtra -= dibExtra;
|
|
if (ibExtra < 0)
|
|
{
|
|
pbRow -= cbRowSrc;
|
|
ibExtra += crcWidth;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
lw = _LwNextDissolve(lw);
|
|
}
|
|
while (0 > (irc = crcFill - lw));
|
|
|
|
#ifdef IN_80386
|
|
__asm
|
|
{
|
|
// ibExtra = irc % crcWidth;
|
|
// pbRow = prgbSrc + (irc / crcWidth) * cbRowSrc;
|
|
mov edx,irc
|
|
movzx eax,dx
|
|
shr edx,16
|
|
div WORD PTR crcWidth // 16 bit divide for speed
|
|
imul eax,cbRowSrc
|
|
mov ibExtra,edx
|
|
add eax,prgbSrc
|
|
mov pbRow,eax
|
|
}
|
|
#else //!IN_80386
|
|
ibExtra = irc % crcWidth;
|
|
pbRow = prgbSrc + (irc / crcWidth) * cbRowSrc;
|
|
#endif //!IN_80386
|
|
}
|
|
|
|
prgbDst[irc] = pbRow[ibExtra];
|
|
}
|
|
|
|
LBlastToScreen:
|
|
CopyPixels(pgnv, prcDst, prcDst);
|
|
GPT::Flush();
|
|
|
|
LPaletteTrans:
|
|
if (cact == 1 && pglclr != pvNil)
|
|
_PaletteTrans(pglclrOld, pglclr, dtsT, dts, pglclrTrans);
|
|
}
|
|
|
|
LSetPalette:
|
|
if (pvNil != pglclr)
|
|
{
|
|
// set the palette
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
|
|
// if we're not in 8 bit and cact is 1, copy the pixels so we
|
|
// make sure we've drawn the picture after the last palette change
|
|
if (pvNil != pgnv && 1 == cact && _pgpt->CbitPixel() != 8)
|
|
{
|
|
CopyPixels(pgnv, prcDst, prcDst);
|
|
GPT::Flush();
|
|
}
|
|
pglclr = pvNil; // so we don't transition during the second wipe
|
|
}
|
|
}
|
|
|
|
if (pvNil != prgbDst)
|
|
pgnv->Pgpt()->Unlock();
|
|
if (pvNil != prgbSrc && pvNil != pgnvSrc)
|
|
pgnvSrc->Pgpt()->Unlock();
|
|
ReleasePpo(&pgnv);
|
|
ReleasePpo(&pglclrOld);
|
|
ReleasePpo(&pglclrTrans);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fade the palette to color acrFade, copy the pixels from pgnvSrc to
|
|
this gnv, then fade to the new palette or original palette. Each fade
|
|
is given dts time. Asserts that acrFade is an rgb color. cactMax is
|
|
the maximum number of palette interpolations to do. It doesn't make
|
|
sense for this to be bigger than 256. If it's zero, we'll use 256.
|
|
***************************************************************************/
|
|
void GNV::Fade(long cactMax, ACR acrFade, PGNV pgnvSrc, RC *prcSrc, RC *prcDst,
|
|
ulong dts, PGL pglclr)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cactMax, 0, 257);
|
|
AssertPo(&acrFade, facrRgb);
|
|
AssertPo(pgnvSrc, 0);
|
|
AssertVarMem(prcSrc);
|
|
AssertVarMem(prcDst);
|
|
AssertNilOrPo(pglclr, 0);
|
|
|
|
ulong tsStart;
|
|
long cact, cactOld;
|
|
CLR clr;
|
|
PGL pglclrOld = pvNil;
|
|
PGL pglclrTrans = pvNil;
|
|
|
|
cactMax = (cactMax <= 0) ? 256 : LwMin(cactMax, 256);
|
|
|
|
if (!_FInitPaletteTrans(pglclr, &pglclrOld, &pglclrTrans, 8))
|
|
{
|
|
CopyPixels(pgnvSrc, prcSrc, prcDst);
|
|
GPT::Flush();
|
|
return;
|
|
}
|
|
|
|
GPT::Flush();
|
|
|
|
if (!FIn(dts, 1, kdtsMaxTrans))
|
|
dts = kdtsSecond;
|
|
|
|
acrFade.GetClr(&clr);
|
|
|
|
tsStart = TsCurrent();
|
|
for (cact = 0; cact < cactMax; )
|
|
{
|
|
cactOld = cact;
|
|
cact = LwMulDiv(cactMax, LwMin(TsCurrent() - tsStart, dts), dts);
|
|
if (cactOld < cact)
|
|
_PaletteTrans(pglclrOld, pvNil, cact, cactMax, pglclrTrans, &clr);
|
|
}
|
|
|
|
CopyPixels(pgnvSrc, prcSrc, prcDst);
|
|
GPT::Flush();
|
|
|
|
if (pvNil == pglclr)
|
|
pglclr = pglclrOld;
|
|
|
|
tsStart = TsCurrent();
|
|
for (cact = 0; cact < cactMax; )
|
|
{
|
|
cactOld = cact;
|
|
cact = LwMulDiv(cactMax, LwMin(TsCurrent() - tsStart, dts), dts);
|
|
if (cactOld < cact)
|
|
_PaletteTrans(pvNil, pglclr, cact, cactMax, pglclrTrans, &clr);
|
|
}
|
|
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
ReleasePpo(&pglclrOld);
|
|
ReleasePpo(&pglclrTrans);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Open and/or close a rectangular iris onto the gnvSrc with an
|
|
intermediate color of acrFill (if not clear). xp, yp are the focus
|
|
point of the iris (in destination coordinates).
|
|
***************************************************************************/
|
|
void GNV::Iris(long gfd, long xp, long yp, ACR acrFill,
|
|
PGNV pgnvSrc, RC *prcSrc, RC *prcDst, ulong dts, PGL pglclr)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(&acrFill, 0);
|
|
AssertPo(pgnvSrc, 0);
|
|
AssertVarMem(prcSrc);
|
|
AssertVarMem(prcDst);
|
|
AssertNilOrPo(pglclr, 0);
|
|
|
|
ulong tsStart, dtsT;
|
|
RC rc, rcOld, rcDst;
|
|
PT pt, ptBase;
|
|
long cact;
|
|
bool fOpen;
|
|
PREGN pregn, pregnClip;
|
|
PGL pglclrOld = pvNil;
|
|
PGL pglclrTrans = pvNil;
|
|
|
|
GPT::Flush();
|
|
|
|
if (pvNil == (pregn = REGN::PregnNew(prcDst)))
|
|
goto LFail;
|
|
|
|
if (!FIn(dts, 1, kdtsMaxTrans))
|
|
dts = kdtsSecond;
|
|
|
|
_pgpt->GetPtBase(&ptBase);
|
|
|
|
pt.xp = LwBound(xp, prcDst->xpLeft, prcDst->xpRight + 1);
|
|
pt.yp = LwBound(yp, prcDst->ypTop, prcDst->ypBottom + 1);
|
|
pt.Map(&_rcSrc, &_rcDst);
|
|
pt += ptBase;
|
|
rcDst = *prcDst;
|
|
rcDst.Map(&_rcSrc, &_rcDst);
|
|
rcDst.Offset(ptBase.xp, ptBase.yp);
|
|
|
|
for (cact = 0; cact < 2; cact++)
|
|
{
|
|
if (cact == 0)
|
|
{
|
|
if (kacrClear == acrFill)
|
|
continue;
|
|
}
|
|
else if (pvNil != pglclr &&
|
|
!_FInitPaletteTrans(pglclr, &pglclrOld, &pglclrTrans, 8))
|
|
{
|
|
pglclr = pvNil; // so we don't try to transition
|
|
}
|
|
|
|
fOpen = !(gfd & (1 << cact));
|
|
if (fOpen)
|
|
rc.Set(pt.xp, pt.yp, pt.xp, pt.yp);
|
|
else
|
|
rc = rcDst;
|
|
|
|
pregnClip = pvNil;
|
|
tsStart = TsCurrent();
|
|
for (dtsT = 0; dtsT < dts; )
|
|
{
|
|
rcOld = rc;
|
|
dtsT = LwMin(TsCurrent() - tsStart, dts);
|
|
if (!fOpen)
|
|
dtsT = dts - dtsT;
|
|
rc.xpLeft = pt.xp + LwMulDiv(rcDst.xpLeft - pt.xp, dtsT, dts);
|
|
rc.xpRight = pt.xp + LwMulDiv(rcDst.xpRight - pt.xp, dtsT, dts);
|
|
rc.ypTop = pt.yp + LwMulDiv(rcDst.ypTop - pt.yp, dtsT, dts);
|
|
rc.ypBottom = pt.yp + LwMulDiv(rcDst.ypBottom - pt.yp, dtsT, dts);
|
|
if (!fOpen)
|
|
dtsT = dts - dtsT;
|
|
|
|
if (cact == 1 && pglclr != pvNil)
|
|
_PaletteTrans(pglclrOld, pglclr, dtsT, dts, pglclrTrans);
|
|
|
|
if (rc == rcOld)
|
|
continue;
|
|
|
|
_pgpt->ClipToRegn(&pregnClip);
|
|
pregn->SetRc(fOpen ? &rc : &rcOld);
|
|
if (!pregn->FDiffRc(fOpen ? &rcOld : &rc) ||
|
|
pvNil != pregnClip && !pregn->FIntersect(pregnClip))
|
|
{
|
|
_pgpt->ClipToRegn(&pregnClip);
|
|
ReleasePpo(&pregn);
|
|
LFail:
|
|
if (pvNil != pglclr)
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
CopyPixels(pgnvSrc, prcSrc, prcDst);
|
|
return;
|
|
}
|
|
_pgpt->ClipToRegn(&pregnClip);
|
|
|
|
_pgpt->ClipToRegn(&pregn);
|
|
if (cact == 0)
|
|
FillRc(prcDst, acrFill);
|
|
else
|
|
CopyPixels(pgnvSrc, prcSrc, prcDst);
|
|
GPT::Flush();
|
|
_pgpt->ClipToRegn(&pregn);
|
|
}
|
|
|
|
if (pvNil != pglclr)
|
|
{
|
|
// set the palette
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
pglclr = pvNil; // so we don't transition during the second iris
|
|
}
|
|
}
|
|
|
|
ReleasePpo(&pregn);
|
|
ReleasePpo(&pglclrOld);
|
|
ReleasePpo(&pglclrTrans);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the picture in the given rectangle.
|
|
***************************************************************************/
|
|
void GNV::DrawPic(PPIC ppic, RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(ppic, 0);
|
|
AssertVarMem(prc);
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs))
|
|
return;
|
|
_pgpt->DrawPic(ppic, &rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the mbmp with reference point at the given point.
|
|
***************************************************************************/
|
|
void GNV::DrawMbmp(PMBMP pmbmp, long xp, long yp)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pmbmp, 0);
|
|
RC rc;
|
|
RCS rcs;
|
|
|
|
pmbmp->GetRc(&rc);
|
|
rc.Offset(xp - rc.xpLeft, yp - rc.ypTop);
|
|
if (!_FMapRcRcs(&rc, &rcs))
|
|
return;
|
|
_pgpt->DrawMbmp(pmbmp, &rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the mbmp in the given rectangle.
|
|
***************************************************************************/
|
|
void GNV::DrawMbmp(PMBMP pmbmp, RC *prc)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pmbmp, 0);
|
|
AssertVarMem(prc);
|
|
RCS rcs;
|
|
|
|
if (!_FMapRcRcs(prc, &rcs))
|
|
return;
|
|
_pgpt->DrawMbmp(pmbmp, &rcs, &_gdd);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Map a rectangle to a system rectangle. Return true iff the result
|
|
is non-empty.
|
|
***************************************************************************/
|
|
bool GNV::_FMapRcRcs(RC *prc, RCS *prcs)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prc);
|
|
AssertVarMem(prcs);
|
|
RC rc = *prc;
|
|
|
|
rc.Map(&_rcSrc, &_rcDst);
|
|
*prcs = RCS(rc);
|
|
return prcs->left < prcs->right && prcs->top < prcs->bottom;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Map an (xp, yp) pair to a system point.
|
|
***************************************************************************/
|
|
void GNV::_MapPtPts(long xp, long yp, PTS *ppts)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ppts);
|
|
PT pt(xp, yp);
|
|
|
|
pt.Map(&_rcSrc, &_rcDst);
|
|
*ppts = PTS(pt);
|
|
}
|
|
|
|
|
|
#ifdef MAC
|
|
/***************************************************************************
|
|
Set the port associated with the GNV as the current port and set the
|
|
clipping as in the GNV and set the pen and fore/back color to defaults.
|
|
***************************************************************************/
|
|
void GNV::Set(void)
|
|
{
|
|
AssertThis(0);
|
|
_pgpt->Set(_gdd.prcsClip);
|
|
ForeColor(blackColor);
|
|
BackColor(whiteColor);
|
|
PenNormal();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Restore the port. Balances a call to GNV::Set.
|
|
***************************************************************************/
|
|
void GNV::Restore(void)
|
|
{
|
|
_pgpt->Restore();
|
|
}
|
|
#endif //MAC
|
|
|
|
|
|
/***************************************************************************
|
|
Clip to the region specified by *ppregn. *ppregn is set to the previous
|
|
clip region (may be pvNil). The GPT takes over ownership of the region
|
|
and relinquishes ownership of the old region.
|
|
***************************************************************************/
|
|
void GPT::ClipToRegn(PREGN *ppregn)
|
|
{
|
|
if (_ptBase.xp != 0 || _ptBase.yp != 0)
|
|
{
|
|
if (pvNil != *ppregn)
|
|
(*ppregn)->Offset(-_ptBase.xp, -_ptBase.yp);
|
|
if (pvNil != _pregnClip)
|
|
_pregnClip->Offset(_ptBase.xp, _ptBase.yp);
|
|
}
|
|
SwapVars(&_pregnClip, ppregn);
|
|
_fNewClip = fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the base PT for the GPT. This affects the mapping of any attached
|
|
GNVs.
|
|
***************************************************************************/
|
|
void GPT::SetPtBase(PT *ppt)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ppt);
|
|
_ptBase = *ppt;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the base PT for the GPT.
|
|
***************************************************************************/
|
|
void GPT::GetPtBase(PT *ppt)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ppt);
|
|
*ppt = _ptBase;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Mark memory for the GPT.
|
|
***************************************************************************/
|
|
void GPT::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
GPT_PAR::MarkMem();
|
|
MarkMemObj(_pregnClip);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Assert the validity of a polygon descriptor.
|
|
******************************************************************************/
|
|
void OLY::AssertValid(ulong grf)
|
|
{
|
|
AssertThisMem();
|
|
AssertPvCb(rgpts, LwMul(Cpts(), size(PTS)));
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Assert the validity of the font description.
|
|
******************************************************************************/
|
|
void DSF::AssertValid(ulong grf)
|
|
{
|
|
AssertThisMem();
|
|
AssertIn(dyp, 1, kswMax);
|
|
AssertVar(vntl.FValidOnn(onn), "Invalid onn", &onn);
|
|
AssertIn(tah, 0, tahLim);
|
|
AssertIn(tav, 0, tavLim);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/******************************************************************************
|
|
Initialize the graphics module.
|
|
******************************************************************************/
|
|
bool FInitGfx(void)
|
|
{
|
|
return vntl.FInit();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Construct a new font list.
|
|
***************************************************************************/
|
|
NTL::NTL(void)
|
|
{
|
|
_pgst = pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destroy a font list.
|
|
***************************************************************************/
|
|
NTL::~NTL(void)
|
|
{
|
|
ReleasePpo(&_pgst);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the font list.
|
|
***************************************************************************/
|
|
void NTL::AssertValid(ulong grf)
|
|
{
|
|
NTL_PAR::AssertValid(0);
|
|
AssertPo(_pgst, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the font table.
|
|
***************************************************************************/
|
|
void NTL::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
NTL_PAR::MarkMem();
|
|
MarkMemObj(_pgst);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return whether the font number is valid.
|
|
***************************************************************************/
|
|
bool NTL::FValidOnn(long onn)
|
|
{
|
|
return pvNil != _pgst && onn >= 0 && onn < _pgst->IstnMac();
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Find the name of the given font.
|
|
***************************************************************************/
|
|
void NTL::GetStn(long onn, PSTN pstn)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstn, 0);
|
|
|
|
_pgst->GetStn(onn, pstn);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the font number for the given font name.
|
|
***************************************************************************/
|
|
bool NTL::FGetOnn(PSTN pstn, long *ponn)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstn, 0);
|
|
|
|
return _pgst->FFindStn(pstn, ponn, fgstUserSorted);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Map a font name to an existing font. Use platform information if
|
|
possible.
|
|
REVIEW shonk: implement font mapping for real.
|
|
***************************************************************************/
|
|
long NTL::OnnMapStn(PSTN pstn, short osk)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstn, 0);
|
|
long onn;
|
|
|
|
if (!FGetOnn(pstn, &onn))
|
|
onn = _onnSystem;
|
|
return onn;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the font number mac.
|
|
***************************************************************************/
|
|
long NTL::OnnMac(void)
|
|
{
|
|
AssertThis(0);
|
|
return _pgst->IstnMac();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a new polygon by tracing the outline of this one with a
|
|
convex polygon.
|
|
***************************************************************************/
|
|
POGN OGN::PognTraceOgn(POGN pogn, ulong grfogn)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pogn, 0);
|
|
|
|
POGN pognNew = PognTraceRgpt(pogn->PrgptLock(), pogn->IvMac(), grfogn);
|
|
|
|
pogn->Unlock();
|
|
return pognNew;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Create a new polygon by tracing the outline of this one with a
|
|
convex polygon.
|
|
***************************************************************************/
|
|
POGN OGN::PognTraceRgpt(PT *prgpt, long cpt, ulong grfogn)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cpt, 2, kcbMax);
|
|
AssertPvCb(prgpt, LwMul(cpt, size(PT)));
|
|
|
|
PT *prgptThis;
|
|
AEI aei;
|
|
long iptLast = IvMac() - 1;
|
|
|
|
if (2 > cpt || iptLast < 0)
|
|
return pvNil;
|
|
|
|
aei.prgpt = prgpt;
|
|
aei.cpt = cpt;
|
|
if (pvNil == (aei.pogn = PognNew(IvMac() * 2 + cpt)))
|
|
return pvNil;
|
|
aei.pogn->SetMinGrow(cpt);
|
|
|
|
prgptThis = PrgptLock();
|
|
if (iptLast == 0 || prgptThis[0] == prgptThis[1])
|
|
{
|
|
//if all the points of the polygon are the same, doing this ensures
|
|
//that we will encircle the pen
|
|
aei.ptCur = prgpt[0] + prgptThis[0];
|
|
if (!aei.pogn->FPush(&aei.ptCur))
|
|
goto LError;
|
|
aei.iptPenCur = 1;
|
|
}
|
|
else
|
|
{
|
|
aei.iptPenCur = IptFindLeftmost(prgpt, cpt,
|
|
prgptThis[0].xp - prgptThis[1].xp,
|
|
prgptThis[0].yp - prgptThis[1].yp);
|
|
}
|
|
aei.ptCur = prgpt[aei.iptPenCur] + prgptThis[0];
|
|
if (!aei.pogn->FPush(&aei.ptCur))
|
|
goto LError;
|
|
|
|
// Walk to end, adding vertices.
|
|
for (aei.dipt = 1, aei.ipt = 0; aei.ipt < iptLast; aei.ipt++)
|
|
{
|
|
if (!_FAddEdge(&aei))
|
|
goto LError;
|
|
}
|
|
if (fognAutoClose & grfogn)
|
|
{
|
|
if (!_FAddEdge(&aei))
|
|
goto LError;
|
|
aei.ipt = 0;
|
|
aei.dipt = iptLast;
|
|
if (!_FAddEdge(&aei))
|
|
goto LError;
|
|
}
|
|
|
|
// Walk back to start.
|
|
for (aei.dipt = aei.ipt = iptLast; aei.ipt > 0; aei.ipt--)
|
|
{
|
|
if (!_FAddEdge(&aei))
|
|
{
|
|
LError:
|
|
ReleasePpo(&aei.pogn);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
if (pvNil != aei.pogn)
|
|
aei.pogn->FEnsureSpace(0, fgrpShrink);
|
|
return aei.pogn;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Add the vertices encountered while walking an edge of the input polygon.
|
|
***************************************************************************/
|
|
bool OGN::_FAddEdge(AEI *paei)
|
|
{
|
|
AssertVarMem(paei);
|
|
AssertIn(paei->cpt, 1, kcbMax);
|
|
AssertPvCb(paei->prgpt, LwMul(paei->cpt, size(PT)));
|
|
AssertPo(paei->pogn, 0);
|
|
AssertIn(paei->dipt, LwMin(IvMac() - 1, 1), LwMax(IvMac(), 2));
|
|
AssertIn(paei->ipt , 0, IvMac());
|
|
AssertIn(paei->iptPenCur, 0, paei->cpt);
|
|
|
|
PT * prgptThis = (PT *)QvGet(0); // Already locked in PognTraceRgpt().
|
|
long iptEnd = (paei->ipt + paei->dipt) % IvMac();
|
|
long iptPenNew = IptFindLeftmost(paei->prgpt, paei->cpt,
|
|
prgptThis[iptEnd].xp - prgptThis[paei->ipt].xp,
|
|
prgptThis[iptEnd].yp - prgptThis[paei->ipt].yp);
|
|
|
|
// Add vertices from current to leftmost.
|
|
if (paei->iptPenCur != iptPenNew)
|
|
{
|
|
PT pt;
|
|
long ipt = paei->iptPenCur;
|
|
PT dpt = paei->ptCur - paei->prgpt[ipt];
|
|
|
|
do
|
|
{
|
|
ipt = (ipt + 1) % paei->cpt;
|
|
pt = paei->prgpt[ipt] + dpt;
|
|
if (!paei->pogn->FPush(&pt))
|
|
return fFalse;
|
|
}
|
|
while (ipt != iptPenNew);
|
|
}
|
|
|
|
// Add vertex at endpoint.
|
|
paei->iptPenCur = iptPenNew;
|
|
paei->ptCur = prgptThis[iptEnd] + paei->prgpt[iptPenNew];
|
|
return paei->pogn->FPush(&paei->ptCur);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find the leftmost vertex of the rgpt looking down the vector.
|
|
dxp, dyp : Direction of vector to look down.
|
|
***************************************************************************/
|
|
long IptFindLeftmost(PT *prgpt, long cpt, long dxp, long dyp)
|
|
{
|
|
AssertPvCb(prgpt, LwMul(cpt, size(PT)));
|
|
AssertIn(cpt, 2, kcbMax);
|
|
|
|
long ipt, iptLeftmost;
|
|
long dzpMac; // Maximum cross product (z vector) found.
|
|
long dzp;
|
|
|
|
//reduces the chance of overflow
|
|
if (1 < (dzp = LwGcd(dxp, dyp)))
|
|
{
|
|
dxp /= dzp;
|
|
dyp /= dzp;
|
|
}
|
|
|
|
for (dzpMac = klwMin, ipt = 0; ipt < cpt; ipt++)
|
|
{
|
|
dzp = LwMul(dyp, prgpt[ipt].xp) - LwMul(dxp, prgpt[ipt].yp);
|
|
if (dzp > dzpMac)
|
|
{
|
|
dzpMac = dzp;
|
|
iptLeftmost = ipt;
|
|
}
|
|
}
|
|
return iptLeftmost;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
-- Allocate a new OGN and ensure that it has space for cptInit elements.
|
|
***************************************************************************/
|
|
POGN OGN::PognNew(long cptInit)
|
|
{
|
|
AssertIn(cptInit, 0, kcbMax);
|
|
POGN pogn;
|
|
|
|
if (pvNil == (pogn = NewObj OGN()))
|
|
return pvNil;
|
|
if (cptInit > 0 && !pogn->FEnsureSpace(cptInit, fgrpNil))
|
|
{
|
|
ReleasePpo(&pogn);
|
|
return pvNil;
|
|
}
|
|
AssertPo(pogn, 0);
|
|
return pogn;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for OGN.
|
|
***************************************************************************/
|
|
OGN::OGN(void) : GL(size(PT))
|
|
{
|
|
AssertThis(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
This does a 2x stretch blt, clipped to prcClip and pregnClip. The
|
|
clipping is expressed in destination coordinates.
|
|
***************************************************************************/
|
|
void DoubleStretch(byte *prgbSrc, long cbRowSrc, long dypSrc, RC *prcSrc,
|
|
byte *prgbDst, long cbRowDst, long dypDst, long xpDst, long ypDst,
|
|
RC *prcClip, PREGN pregnClip)
|
|
{
|
|
AssertPvCb(prgbSrc, LwMul(cbRowSrc, dypSrc));
|
|
AssertPvCb(prgbDst, LwMul(cbRowDst, dypDst));
|
|
AssertVarMem(prcSrc);
|
|
Assert(prcSrc->xpLeft >= 0 && prcSrc->ypTop >= 0 &&
|
|
prcSrc->xpRight <= cbRowSrc && prcSrc->ypBottom <= dypSrc,
|
|
"Source rectangle not in source bitmap!");
|
|
AssertNilOrVarMem(prcClip);
|
|
AssertNilOrPo(pregnClip, 0);
|
|
|
|
long xpOn, xpOff, dypAdvance, dxpBase, yp;
|
|
bool fSecondRow;
|
|
REGSC regsc;
|
|
RC rcT(xpDst, ypDst, xpDst + 2 * prcSrc->Dxp(), ypDst + 2 * prcSrc->Dyp());
|
|
RC rcClip(0, 0, cbRowDst, dypDst);
|
|
|
|
if (!rcClip.FIntersect(&rcT))
|
|
return;
|
|
if (pvNil != prcClip && !rcClip.FIntersect(prcClip))
|
|
return;
|
|
|
|
// Set up the region scanner
|
|
if (pvNil != pregnClip)
|
|
regsc.Init(pregnClip, &rcClip);
|
|
else
|
|
regsc.InitRc(&rcClip, &rcClip);
|
|
dxpBase = rcClip.xpLeft - xpDst;
|
|
|
|
// move to rcClip.ypTop
|
|
yp = rcClip.ypTop;
|
|
prgbSrc += prcSrc->xpLeft +
|
|
LwMul(prcSrc->ypTop + ((yp - ypDst) >> 1), cbRowSrc);
|
|
prgbDst += xpDst + LwMul(yp, cbRowDst);
|
|
fSecondRow = (yp - ypDst) & 1;
|
|
|
|
for (;;)
|
|
{
|
|
if (klwMax == (xpOn = regsc.XpCur()))
|
|
{
|
|
//empty strip of the region
|
|
dypAdvance = regsc.DypCur();
|
|
goto LAdvance;
|
|
}
|
|
xpOn += dxpBase;
|
|
if (fSecondRow || (regsc.DypCur() == 1))
|
|
goto LOneRow;
|
|
|
|
// copy two rows to the destination
|
|
for (;;)
|
|
{
|
|
xpOff = regsc.XpFetch() + dxpBase;
|
|
AssertIn(xpOff - 1, xpOn, rcClip.Dxp() + dxpBase);
|
|
|
|
#ifdef IN_80386
|
|
// copy two rows to the destination in native
|
|
#define pbDstReg edi
|
|
#define pbSrcReg esi
|
|
#define pbDst2Reg ebx
|
|
#define lwTReg edx
|
|
|
|
__asm
|
|
{
|
|
// lwTReg = xpOn;
|
|
// pbDstReg = prgbDst + xpOn;
|
|
// pbSrcReg = prgbSrc + (xpOn >> 1);
|
|
// pbDst2Reg = pbDstReg + cbRowDst;
|
|
mov lwTReg,xpOn
|
|
mov pbSrcReg,lwTReg
|
|
mov pbDstReg,lwTReg
|
|
sar pbSrcReg,1
|
|
add pbDstReg,prgbDst
|
|
add pbSrcReg,prgbSrc
|
|
mov pbDst2Reg,pbDstReg
|
|
add pbDst2Reg,cbRowDst
|
|
|
|
// if (!(lwTReg & 1)) goto LGetCount2;
|
|
test lwTReg,1
|
|
mov ecx,xpOff
|
|
jz LGetCount2
|
|
|
|
// move the single leading byte
|
|
// lwTReg++;
|
|
// al = *pbSrcReg++;
|
|
// *pbDstReg++ = al;
|
|
// *pbDstReg2++ = al;
|
|
inc lwTReg
|
|
mov al,[pbSrcReg]
|
|
inc pbSrcReg
|
|
mov [pbDstReg],al
|
|
inc pbDstReg
|
|
mov [pbDst2Reg],al
|
|
inc pbDst2Reg
|
|
|
|
LGetCount2:
|
|
// ecx = xpOff - lwTReg;
|
|
// ecx >>= 1;
|
|
// if (ecx <= 0) goto LLastByte2;
|
|
sub ecx,lwTReg
|
|
sar ecx,1
|
|
jle LLastByte2
|
|
|
|
LLoop2:
|
|
// al = *pbSrcReg++;
|
|
// ah = al;
|
|
// *(short *)pbDstReg = ax;
|
|
// *(short *)pbDst2Reg = ax;
|
|
// pbDstReg += 2;
|
|
// pbDst2Reg += 2;
|
|
mov al,[pbSrcReg]
|
|
inc pbSrcReg
|
|
mov ah,al
|
|
mov [pbDstReg],ax
|
|
add pbDstReg,2
|
|
mov [pbDst2Reg],ax
|
|
add pbDst2Reg,2
|
|
|
|
// if (--ecx != 0) goto LLoop2;
|
|
loop LLoop2
|
|
|
|
LLastByte2:
|
|
// if (!(xpOff & 1)) goto LDone2;
|
|
test xpOff,1
|
|
jz LDone2
|
|
|
|
// move the single trailing byte
|
|
// al = *pbSrcReg;
|
|
// *pbDstReg = al;
|
|
// *pbDstReg2 = al;
|
|
mov al,[pbSrcReg]
|
|
mov [pbDstReg],al
|
|
mov [pbDst2Reg],al
|
|
LDone2:
|
|
}
|
|
|
|
#undef pbDstReg
|
|
#undef pbSrcReg
|
|
#undef pbDst2Reg
|
|
#undef lwTReg
|
|
#else //!IN_80386
|
|
// copy two rows to the destination in C code
|
|
byte *pbSrc = prgbSrc + (xpOn >> 1);
|
|
byte *pbDst = prgbDst + xpOn;
|
|
byte *pbDst2 = pbDst + cbRowDst;
|
|
byte bT;
|
|
long cactLoop;
|
|
|
|
if (xpOn & 1)
|
|
{
|
|
// do leading single byte
|
|
bT = *pbSrc++;
|
|
*pbDst++ = bT;
|
|
*pbDst2++ = bT;
|
|
xpOn++;
|
|
}
|
|
|
|
for (cactLoop = (xpOff - xpOn) >> 1; cactLoop > 0; cactLoop--)
|
|
{
|
|
bT = *pbSrc++;
|
|
pbDst[0] = bT;
|
|
pbDst[1] = bT;
|
|
pbDst += 2;
|
|
pbDst2[0] = bT;
|
|
pbDst2[1] = bT;
|
|
pbDst2 += 2;
|
|
}
|
|
|
|
if (xpOff & 1)
|
|
{
|
|
// do the trailing byte
|
|
bT = *pbSrc;
|
|
*pbDst = bT;
|
|
*pbDst2 = bT;
|
|
}
|
|
#endif //!IN_80386
|
|
|
|
if (klwMax == (xpOn = regsc.XpFetch()))
|
|
break;
|
|
xpOn += dxpBase;
|
|
AssertIn(xpOn - 1, xpOff, rcClip.Dxp() - 1 + dxpBase);
|
|
}
|
|
dypAdvance = 2;
|
|
goto LAdvance;
|
|
|
|
LOneRow:
|
|
// copy just one row to the destination
|
|
for (;;)
|
|
{
|
|
xpOff = regsc.XpFetch() + dxpBase;
|
|
AssertIn(xpOff - 1, xpOn, rcClip.Dxp() + dxpBase);
|
|
|
|
#ifdef IN_80386
|
|
// copy just one row to the destination in native
|
|
#define pbDstReg edi
|
|
#define pbSrcReg esi
|
|
#define lwTReg edx
|
|
|
|
__asm
|
|
{
|
|
// lwTReg = xpOn;
|
|
// pbDstReg = prgbDst + xpOn;
|
|
// pbSrcReg = prgbSrc + (xpOn >> 1);
|
|
mov lwTReg,xpOn
|
|
mov pbSrcReg,lwTReg
|
|
mov pbDstReg,lwTReg
|
|
sar pbSrcReg,1
|
|
add pbDstReg,prgbDst
|
|
add pbSrcReg,prgbSrc
|
|
|
|
// if (!(lwTReg & 1)) goto LGetCount1;
|
|
test lwTReg,1
|
|
mov ecx,xpOff
|
|
jz LGetCount1
|
|
|
|
// move the single leading byte
|
|
// lwTReg++;
|
|
// al = *pbSrcReg++;
|
|
// *pbDstReg++ = al;
|
|
inc lwTReg
|
|
mov al,[pbSrcReg]
|
|
inc pbSrcReg
|
|
inc pbDstReg
|
|
mov [pbDstReg-1],al
|
|
|
|
LGetCount1:
|
|
// ecx = xpOff - lwTReg;
|
|
// ecx >>= 1;
|
|
// if (ecx <= 0) goto LLastByte1;
|
|
sub ecx,lwTReg
|
|
sar ecx,1
|
|
jle LLastByte1
|
|
|
|
LLoop1:
|
|
// al = *pbSrcReg++;
|
|
// ah = al;
|
|
// *(short *)pbDstReg = ax;
|
|
// pbDstReg += 2;
|
|
mov al,[pbSrcReg]
|
|
inc pbSrcReg
|
|
add pbDstReg,2
|
|
mov ah,al
|
|
mov [pbDstReg-2],ax
|
|
|
|
// if (--ecx != 0) goto LLoop1;
|
|
loop LLoop1
|
|
|
|
LLastByte1:
|
|
// if (!(xpOff & 1)) goto LDone1;
|
|
test xpOff,1
|
|
jz LDone1
|
|
|
|
// move the single trailing byte
|
|
// al = *pbSrcReg;
|
|
// *pbDstReg = al;
|
|
mov al,[pbSrcReg]
|
|
mov [pbDstReg],al
|
|
LDone1:
|
|
}
|
|
|
|
#undef pbDstReg
|
|
#undef pbSrcReg
|
|
#undef lwTReg
|
|
#else //!IN_80386
|
|
// copy just one row to the destination in C code
|
|
byte bT;
|
|
byte *pbSrc = prgbSrc + (xpOn >> 1);
|
|
byte *pbDst = prgbDst + xpOn;
|
|
long cactLoop;
|
|
|
|
if (xpOn & 1)
|
|
{
|
|
// do leading single byte
|
|
*pbDst++ = *pbSrc++;
|
|
xpOn++;
|
|
}
|
|
|
|
for (cactLoop = (xpOff - xpOn) >> 1; cactLoop > 0; cactLoop--)
|
|
{
|
|
bT = *pbSrc++;
|
|
pbDst[0] = bT;
|
|
pbDst[1] = bT;
|
|
pbDst += 2;
|
|
}
|
|
|
|
if (xpOff & 1)
|
|
{
|
|
// do the trailing byte
|
|
*pbDst = *pbSrc;
|
|
}
|
|
#endif //!IN_80386
|
|
|
|
if (klwMax == (xpOn = regsc.XpFetch()))
|
|
break;
|
|
xpOn += dxpBase;
|
|
AssertIn(xpOn - 1, xpOff, rcClip.Dxp() - 1 + dxpBase);
|
|
}
|
|
dypAdvance = 1;
|
|
|
|
LAdvance:
|
|
if ((yp += dypAdvance) >= rcClip.ypBottom)
|
|
break;
|
|
regsc.ScanNext(dypAdvance);
|
|
prgbDst += dypAdvance * cbRowDst;
|
|
prgbSrc += (dypAdvance >> 1) * cbRowSrc;
|
|
if (dypAdvance & 1)
|
|
{
|
|
if (fSecondRow)
|
|
prgbSrc += cbRowSrc;
|
|
fSecondRow = !fSecondRow;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
This does a 2x vertical and 1x horizontal stretch blt, clipped to prcClip
|
|
and pregnClip. The clipping is expressed in destination coordinates.
|
|
***************************************************************************/
|
|
void DoubleVertStretch(byte *prgbSrc, long cbRowSrc, long dypSrc, RC *prcSrc,
|
|
byte *prgbDst, long cbRowDst, long dypDst, long xpDst, long ypDst,
|
|
RC *prcClip, PREGN pregnClip)
|
|
{
|
|
AssertPvCb(prgbSrc, LwMul(cbRowSrc, dypSrc));
|
|
AssertPvCb(prgbDst, LwMul(cbRowDst, dypDst));
|
|
AssertVarMem(prcSrc);
|
|
Assert(prcSrc->xpLeft >= 0 && prcSrc->ypTop >= 0 &&
|
|
prcSrc->xpRight <= cbRowSrc && prcSrc->ypBottom <= dypSrc,
|
|
"Source rectangle not in source bitmap!");
|
|
AssertNilOrVarMem(prcClip);
|
|
AssertNilOrPo(pregnClip, 0);
|
|
|
|
long xpOn, xpOff, dypAdvance, dxpBase, yp;
|
|
bool fSecondRow;
|
|
REGSC regsc;
|
|
RC rcT(xpDst, ypDst, xpDst + prcSrc->Dxp(), ypDst + 2 * prcSrc->Dyp());
|
|
RC rcClip(0, 0, cbRowDst, dypDst);
|
|
|
|
if (!rcClip.FIntersect(&rcT))
|
|
return;
|
|
if (pvNil != prcClip && !rcClip.FIntersect(prcClip))
|
|
return;
|
|
|
|
// Set up the region scanner
|
|
if (pvNil != pregnClip)
|
|
regsc.Init(pregnClip, &rcClip);
|
|
else
|
|
regsc.InitRc(&rcClip, &rcClip);
|
|
dxpBase = rcClip.xpLeft - xpDst;
|
|
|
|
// move to rcClip.ypTop
|
|
yp = rcClip.ypTop;
|
|
prgbSrc += prcSrc->xpLeft +
|
|
LwMul(prcSrc->ypTop + ((yp - ypDst) >> 1), cbRowSrc);
|
|
prgbDst += xpDst + LwMul(yp, cbRowDst);
|
|
fSecondRow = (yp - ypDst) & 1;
|
|
|
|
for (;;)
|
|
{
|
|
if (klwMax == (xpOn = regsc.XpCur()))
|
|
{
|
|
//empty strip of the region
|
|
dypAdvance = regsc.DypCur();
|
|
goto LAdvance;
|
|
}
|
|
xpOn += dxpBase;
|
|
if (fSecondRow || (regsc.DypCur() == 1))
|
|
goto LOneRow;
|
|
|
|
// copy two rows to the destination
|
|
for (;;)
|
|
{
|
|
xpOff = regsc.XpFetch() + dxpBase;
|
|
AssertIn(xpOff - 1, xpOn, rcClip.Dxp() + dxpBase);
|
|
|
|
#ifdef IN_80386
|
|
|
|
// copy two rows to the destination in native
|
|
__asm
|
|
{
|
|
// edi = prgbDst + xpOn;
|
|
// esi = eax = prgbSrc + xpOn;
|
|
// ebx = pbDstReg + cbRowDst;
|
|
// ecx = xpOff - xpOn
|
|
mov edx,xpOn
|
|
mov ecx,xpOff
|
|
mov edi,edx
|
|
mov esi,edx
|
|
sub ecx,edx
|
|
add edi,prgbDst
|
|
add esi,prgbSrc
|
|
mov ebx,edi
|
|
mov eax,esi
|
|
add ebx,cbRowDst
|
|
|
|
// copy the first row
|
|
mov edx,ecx
|
|
shr ecx,2
|
|
rep movsd
|
|
mov ecx,edx
|
|
and ecx,3
|
|
rep movsb
|
|
|
|
// prepare to copy the second row
|
|
mov esi,eax
|
|
mov edi,ebx
|
|
mov ecx,edx
|
|
|
|
// copy the second row
|
|
shr ecx,2
|
|
and edx,3
|
|
rep movsd
|
|
mov ecx,edx
|
|
rep movsb
|
|
}
|
|
|
|
#else //!IN_80386
|
|
|
|
// copy two rows to the destination in C code
|
|
CopyPb(prgbSrc + xpOn, prgbDst + xpOn, xpOff - xpOn);
|
|
CopyPb(prgbSrc + xpOn + cbRowDst, prgbDst + xpOn, xpOff - xpOn);
|
|
|
|
#endif //!IN_80386
|
|
|
|
if (klwMax == (xpOn = regsc.XpFetch()))
|
|
break;
|
|
xpOn += dxpBase;
|
|
AssertIn(xpOn - 1, xpOff, rcClip.Dxp() - 1 + dxpBase);
|
|
}
|
|
dypAdvance = 2;
|
|
goto LAdvance;
|
|
|
|
LOneRow:
|
|
// copy just one row to the destination
|
|
for (;;)
|
|
{
|
|
xpOff = regsc.XpFetch() + dxpBase;
|
|
AssertIn(xpOff - 1, xpOn, rcClip.Dxp() + dxpBase);
|
|
|
|
#ifdef IN_80386
|
|
|
|
// copy one row to the destination in native
|
|
__asm
|
|
{
|
|
// edi = prgbDst + xpOn;
|
|
// esi = prgbSrc + xpOn;
|
|
// ecx = xpOff - xpOn
|
|
mov edx,xpOn
|
|
mov ecx,xpOff
|
|
mov edi,edx
|
|
mov esi,edx
|
|
sub ecx,edx
|
|
add edi,prgbDst
|
|
add esi,prgbSrc
|
|
|
|
// copy the row
|
|
mov edx,ecx
|
|
shr ecx,2
|
|
and edx,3
|
|
rep movsd
|
|
mov ecx,edx
|
|
rep movsb
|
|
}
|
|
|
|
#else //!IN_80386
|
|
|
|
// copy one row to the destination in C code
|
|
CopyPb(prgbSrc + xpOn, prgbDst + xpOn, xpOff - xpOn);
|
|
|
|
#endif //!IN_80386
|
|
|
|
if (klwMax == (xpOn = regsc.XpFetch()))
|
|
break;
|
|
xpOn += dxpBase;
|
|
AssertIn(xpOn - 1, xpOff, rcClip.Dxp() - 1 + dxpBase);
|
|
}
|
|
dypAdvance = 1;
|
|
|
|
LAdvance:
|
|
if ((yp += dypAdvance) >= rcClip.ypBottom)
|
|
break;
|
|
regsc.ScanNext(dypAdvance);
|
|
prgbDst += dypAdvance * cbRowDst;
|
|
prgbSrc += (dypAdvance >> 1) * cbRowSrc;
|
|
if (dypAdvance & 1)
|
|
{
|
|
if (fSecondRow)
|
|
prgbSrc += cbRowSrc;
|
|
fSecondRow = !fSecondRow;
|
|
}
|
|
}
|
|
}
|
|
|
|
|