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

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;
}
}
}