Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1307 lines
42 KiB

/******************************Module*Header*******************************\
* Module Name: textout.c
*
* Copyright (c) 1992-1995 Microsoft Corporation
*
\**************************************************************************/
#include "precomp.h"
/******************************Public*Routine******************************\
* VOID vClipSolid
*
* Fills the specified rectangles with the specified colour, honouring
* the requested clipping. No more than four rectangles should be passed in.
* Intended for drawing the areas of the opaquing rectangle that extend
* beyond the text box. The rectangles must be in left to right, top to
* bottom order. Assumes there is at least one rectangle in the list.
*
\**************************************************************************/
VOID vClipSolid(
PDEV* ppdev,
LONG crcl,
RECTL* prcl,
ULONG iColor,
CLIPOBJ* pco)
{
BOOL bMore; // Flag for clip enumeration
CLIPENUM ce; // Clip enumeration object
ULONG i;
ULONG j;
RECTL arclTmp[4];
ULONG crclTmp;
RECTL* prclTmp;
RECTL* prclClipTmp;
LONG iLastBottom;
RECTL* prclClip;
RBRUSH_COLOR rbc;
ASSERTDD((crcl > 0) && (crcl <= 4), "Expected 1 to 4 rectangles");
ASSERTDD((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL),
"Expected a non-null clip object");
rbc.iSolidColor = iColor;
if (pco->iDComplexity == DC_RECT)
{
crcl = cIntersect(&pco->rclBounds, prcl, crcl);
if (crcl != 0)
{
(ppdev->pfnFillSolid)(ppdev, crcl, prcl, 0xf0f0, rbc, NULL);
}
}
else // iDComplexity == DC_COMPLEX
{
// Bottom of last rectangle to fill
iLastBottom = prcl[crcl - 1].bottom;
// Initialize the clip rectangle enumeration to right-down so we can
// take advantage of the rectangle list being right-down:
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
// Scan through all the clip rectangles, looking for intersects
// of fill areas with region rectangles:
do {
// Get a batch of region rectangles:
bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (VOID*)&ce);
// Clip the rect list to each region rect:
for (j = ce.c, prclClip = ce.arcl; j-- > 0; prclClip++)
{
// Since the rectangles and the region enumeration are both
// right-down, we can zip through the region until we reach
// the first fill rect, and are done when we've passed the
// last fill rect.
if (prclClip->top >= iLastBottom)
{
// Past last fill rectangle; nothing left to do:
return;
}
// Do intersection tests only if we've reached the top of
// the first rectangle to fill:
if (prclClip->bottom > prcl->top)
{
// We've reached the top Y scan of the first rect, so
// it's worth bothering checking for intersection.
// Generate a list of the rects clipped to this region
// rect:
prclTmp = prcl;
prclClipTmp = arclTmp;
for (i = crcl, crclTmp = 0; i-- != 0; prclTmp++)
{
// Intersect fill and clip rectangles
if (bIntersect(prclTmp, prclClip, prclClipTmp))
{
// Add to list if anything's left to draw:
crclTmp++;
prclClipTmp++;
}
}
// Draw the clipped rects
if (crclTmp != 0)
{
(ppdev->pfnFillSolid)(ppdev, crclTmp, &arclTmp[0],
0xf0f0, rbc, NULL);
}
}
}
} while (bMore);
}
}
/******************************Public*Routine******************************\
* VOID vIoClipText
*
\**************************************************************************/
VOID vIoClipText(
PDEV* ppdev,
STROBJ* pstro,
CLIPOBJ* pco)
{
BYTE* pjIoBase;
BYTE* pjScreen;
BOOL bMoreGlyphs;
ULONG cGlyphOriginal;
ULONG cGlyph;
GLYPHPOS* pgpOriginal;
GLYPHPOS* pgp;
GLYPHBITS* pgb;
POINTL ptlOrigin;
BOOL bMore;
CLIPENUM ce;
RECTL* prclClip;
ULONG ulCharInc;
LONG cxGlyph;
LONG cyGlyph;
LONG xBias;
LONG cx;
LONG cy;
ULONG* pdSrc;
ULONG* pdDst;
LONG cj;
LONG cd;
LONG xLeft;
LONG yTop;
LONG xRight;
LONG yBottom;
LONG lDelta;
LONG i;
BYTE* pjSrc;
ASSERTDD(pco != NULL, "Don't expect NULL clip objects here");
pjIoBase = ppdev->pjIoBase;
pjScreen = ppdev->pjScreen;
do {
if (pstro->pgp != NULL)
{
// There's only the one batch of glyphs, so save ourselves
// a call:
pgpOriginal = pstro->pgp;
cGlyphOriginal = pstro->cGlyphs;
bMoreGlyphs = FALSE;
}
else
{
bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyphOriginal, &pgpOriginal);
}
if (cGlyphOriginal > 0)
{
ulCharInc = pstro->ulCharInc;
if (pco->iDComplexity == DC_RECT)
{
// We could call 'cEnumStart' and 'bEnum' when the clipping is
// DC_RECT, but the last time I checked, those two calls took
// more than 150 instructions to go through GDI. Since
// 'rclBounds' already contains the DC_RECT clip rectangle,
// and since it's such a common case, we'll special case it:
bMore = FALSE;
prclClip = &pco->rclBounds;
ce.c = 1;
goto SingleRectangle;
}
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
do {
bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (ULONG*) &ce);
for (prclClip = &ce.arcl[0]; ce.c != 0; ce.c--, prclClip++)
{
SingleRectangle:
pgp = pgpOriginal;
cGlyph = cGlyphOriginal;
pgb = pgp->pgdf->pgb;
ptlOrigin.x = pgb->ptlOrigin.x + pgp->ptl.x;
ptlOrigin.y = pgb->ptlOrigin.y + pgp->ptl.y;
pdDst = (ULONG*) pjScreen;
IO_WAIT_FOR_IDLE(ppdev, pjIoBase);
// Loop through all the glyphs for this rectangle:
while (TRUE)
{
cxGlyph = pgb->sizlBitmap.cx;
cyGlyph = pgb->sizlBitmap.cy;
pdSrc = (ULONG*) pgb->aj;
if ((prclClip->left <= ptlOrigin.x) &&
(prclClip->top <= ptlOrigin.y) &&
(prclClip->right >= ptlOrigin.x + cxGlyph) &&
(prclClip->bottom >= ptlOrigin.y + cyGlyph))
{
//-----------------------------------------------------
// Unclipped glyph
IO_DEST_XY(ppdev, pjIoBase, ptlOrigin.x, ptlOrigin.y);
IO_BITMAP_WIDTH(ppdev, pjIoBase, cxGlyph);
IO_BITMAP_HEIGHT(ppdev, pjIoBase, cyGlyph);
IO_BLT_CMD_0(ppdev, pjIoBase, START_BLT);
cj = cyGlyph * ((cxGlyph + 7) >> 3);
cd = cj >> 2;
if (cd != 0)
{
do {
WRITE_REGISTER_ULONG(pdDst, *pdSrc);
MEMORY_BARRIER();
pdSrc++;
} while (--cd != 0);
}
// We have to be careful we don't output any more data
// than we're supposed to, otherwise we get garbage on
// the screen:
switch (cj & 3)
{
case 1:
WRITE_REGISTER_UCHAR(pdDst, *(UCHAR*) pdSrc);
MEMORY_BARRIER();
break;
case 2:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
break;
case 3:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
WRITE_REGISTER_UCHAR(pdDst, *((UCHAR*) pdSrc + 2));
MEMORY_BARRIER();
break;
}
IO_WAIT_TRANSFER_DONE(ppdev, pjIoBase);
}
else
{
//-----------------------------------------------------
// Clipped glyph
// Find the intersection of the glyph rectangle
// and the clip rectangle:
xLeft = max(prclClip->left, ptlOrigin.x);
yTop = max(prclClip->top, ptlOrigin.y);
xRight = min(prclClip->right, ptlOrigin.x + cxGlyph);
yBottom = min(prclClip->bottom, ptlOrigin.y + cyGlyph);
// Check for trivial rejection:
if (((cx = xRight - xLeft) > 0) &&
((cy = yBottom - yTop) > 0))
{
IO_DEST_XY(ppdev, pjIoBase, xLeft, yTop);
IO_BITMAP_WIDTH(ppdev, pjIoBase, cx);
IO_BITMAP_HEIGHT(ppdev, pjIoBase, cy);
xBias = (xLeft - ptlOrigin.x) & 7;
cx += xBias;
IO_SRC_ALIGN(ppdev, pjIoBase, xBias);
lDelta = (cxGlyph + 7) >> 3;
pjSrc = (BYTE*) pdSrc + (yTop - ptlOrigin.y) * lDelta
+ ((xLeft - ptlOrigin.x) >> 3);
cj = (cx + 7) >> 3;
lDelta -= cj;
IO_BLT_CMD_0(ppdev, pjIoBase, START_BLT);
do {
i = cj;
do {
WRITE_REGISTER_UCHAR((UCHAR*) pdDst, *pjSrc);
MEMORY_BARRIER();
pjSrc++;
} while (--i != 0);
pjSrc += lDelta;
} while (--cy != 0);
// Reset the alignment register in case we have more
// unclipped glyphs in this string:
IO_SRC_ALIGN(ppdev, pjIoBase, 0);
IO_WAIT_TRANSFER_DONE(ppdev, pjIoBase);
}
}
if (--cGlyph == 0)
break;
// Get ready for next glyph:
pgp++;
pgb = pgp->pgdf->pgb;
if (ulCharInc == 0)
{
ptlOrigin.x = pgp->ptl.x + pgb->ptlOrigin.x;
ptlOrigin.y = pgp->ptl.y + pgb->ptlOrigin.y;
}
else
{
ptlOrigin.x += ulCharInc;
}
}
}
} while (bMore);
}
} while (bMoreGlyphs);
}
/******************************Public*Routine******************************\
* VOID vIoTextOut
*
\**************************************************************************/
VOID vIoTextOut(
PDEV* ppdev,
STROBJ* pstro,
FONTOBJ* pfo,
CLIPOBJ* pco,
RECTL* prclOpaque,
BRUSHOBJ* pboFore,
BRUSHOBJ* pboOpaque)
{
BYTE* pjIoBase;
BOOL bTextPerfectFit;
ULONG cGlyph;
BOOL bMoreGlyphs;
GLYPHPOS* pgp;
GLYPHBITS* pgb;
LONG cxGlyph;
LONG cyGlyph;
ULONG* pdSrc;
ULONG* pdDst;
BYTE* pjScreen;
LONG cj;
LONG cd;
POINTL ptlOrigin;
LONG ulCharInc;
BYTE iDComplexity;
pjIoBase = ppdev->pjIoBase;
pjScreen = ppdev->pjScreen;
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
if (prclOpaque != NULL)
{
////////////////////////////////////////////////////////////
// Opaque Initialization
////////////////////////////////////////////////////////////
// If we paint the glyphs in 'opaque' mode, we may not actually
// have to draw the opaquing rectangle up-front -- the process
// of laying down all the glyphs will automatically cover all
// of the pixels in the opaquing rectangle.
//
// The condition that must be satisfied is that the text must
// fit 'perfectly' such that the entire background rectangle is
// covered, and none of the glyphs overlap (if the glyphs
// overlap, such as for italics, they have to be drawn in
// transparent mode after the opaquing rectangle is cleared).
bTextPerfectFit = (pstro->flAccel & (SO_ZERO_BEARINGS |
SO_FLAG_DEFAULT_PLACEMENT | SO_MAXEXT_EQUAL_BM_SIDE |
SO_CHAR_INC_EQUAL_BM_BASE)) ==
(SO_ZERO_BEARINGS | SO_FLAG_DEFAULT_PLACEMENT |
SO_MAXEXT_EQUAL_BM_SIDE | SO_CHAR_INC_EQUAL_BM_BASE);
if (!(bTextPerfectFit) ||
(pstro->rclBkGround.top > prclOpaque->top) ||
(pstro->rclBkGround.left > prclOpaque->left) ||
(pstro->rclBkGround.right < prclOpaque->right) ||
(pstro->rclBkGround.bottom < prclOpaque->bottom))
{
if (iDComplexity == DC_TRIVIAL)
{
IO_WAIT_FOR_IDLE(ppdev, pjIoBase);
IO_PREG_COLOR_8(ppdev, pjIoBase, pboOpaque->iSolidColor);
IO_CTRL_REG_1(ppdev, pjIoBase, PACKED_PIXEL_VIEW |
BITS_PER_PIX_8 |
ENAB_TRITON_MODE);
IO_BLT_CMD_1(ppdev, pjIoBase, XY_SRC_ADDR |
XY_DEST_ADDR);
IO_DATAPATH_CTRL(ppdev, pjIoBase, ROPSELECT_NO_ROPS |
PIXELMASK_ONLY |
PLANARMASK_NONE_0XFF |
SRC_IS_PATTERN_REGS);
IO_BITMAP_HEIGHT(ppdev, pjIoBase, prclOpaque->bottom - prclOpaque->top);
IO_BITMAP_WIDTH(ppdev, pjIoBase, prclOpaque->right - prclOpaque->left);
IO_DEST_XY(ppdev, pjIoBase, prclOpaque->left, prclOpaque->top);
IO_BLT_CMD_0(ppdev, pjIoBase, START_BLT);
}
else
{
vClipSolid(ppdev, 1, prclOpaque, pboOpaque->iSolidColor, pco);
}
}
if (bTextPerfectFit)
{
// If we have already drawn the opaquing rectangle (because
// is was larger than the text rectangle), we could lay down
// the glyphs in 'transparent' mode. But I've found the QVision
// to be a bit faster drawing in opaque mode, so we'll stick
// with that:
IO_WAIT_FOR_IDLE(ppdev, pjIoBase);
IO_CTRL_REG_1(ppdev, pjIoBase, EXPAND_TO_FG |
BITS_PER_PIX_8 |
ENAB_TRITON_MODE);
IO_BLT_CMD_1(ppdev, pjIoBase, XY_SRC_ADDR |
XY_DEST_ADDR);
IO_DATAPATH_CTRL(ppdev, pjIoBase, ROPSELECT_NO_ROPS |
PIXELMASK_ONLY |
PLANARMASK_NONE_0XFF |
SRC_IS_CPU_DATA);
IO_FG_COLOR(ppdev, pjIoBase, pboFore->iSolidColor);
IO_BG_COLOR(ppdev, pjIoBase, pboOpaque->iSolidColor);
IO_SRC_ALIGN(ppdev, pjIoBase, 0);
goto SkipTransparentInitialization;
}
}
////////////////////////////////////////////////////////////
// Transparent Initialization
////////////////////////////////////////////////////////////
// Initialize the hardware for transparent text:
IO_WAIT_FOR_IDLE(ppdev, pjIoBase);
IO_CTRL_REG_1(ppdev, pjIoBase, EXPAND_TO_FG |
BITS_PER_PIX_8 |
ENAB_TRITON_MODE);
IO_BLT_CMD_1(ppdev, pjIoBase, XY_SRC_ADDR |
XY_DEST_ADDR);
IO_DATAPATH_CTRL(ppdev, pjIoBase, ROPSELECT_NO_ROPS |
PIXELMASK_AND_SRC_DATA |
PLANARMASK_NONE_0XFF |
SRC_IS_CPU_DATA);
IO_FG_COLOR(ppdev, pjIoBase, pboFore->iSolidColor);
IO_SRC_ALIGN(ppdev, pjIoBase, 0);
SkipTransparentInitialization:
if (iDComplexity == DC_TRIVIAL)
{
do {
if (pstro->pgp != NULL)
{
// There's only the one batch of glyphs, so save ourselves
// a call:
pgp = pstro->pgp;
cGlyph = pstro->cGlyphs;
bMoreGlyphs = FALSE;
}
else
{
bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyph, &pgp);
}
if (cGlyph > 0)
{
if (pstro->ulCharInc == 0)
{
////////////////////////////////////////////////////////////
// Proportional Spacing
pdDst = (ULONG*) pjScreen;
IO_WAIT_FOR_IDLE(ppdev, pjIoBase);
do {
pgb = pgp->pgdf->pgb;
IO_DEST_XY(ppdev, pjIoBase, pgp->ptl.x + pgb->ptlOrigin.x,
pgp->ptl.y + pgb->ptlOrigin.y);
cxGlyph = pgb->sizlBitmap.cx;
IO_BITMAP_WIDTH(ppdev, pjIoBase, cxGlyph);
cyGlyph = pgb->sizlBitmap.cy;
IO_BITMAP_HEIGHT(ppdev, pjIoBase, cyGlyph);
IO_BLT_CMD_0(ppdev, pjIoBase, START_BLT);
pdSrc = (ULONG*) pgb->aj;
cj = cyGlyph * ((cxGlyph + 7) >> 3);
cd = cj >> 2;
if (cd != 0)
{
do {
WRITE_REGISTER_ULONG(pdDst, *pdSrc);
MEMORY_BARRIER();
pdSrc++;
} while (--cd != 0);
}
// We have to be careful we don't output any more data
// than we're supposed to, otherwise we get garbage on
// the screen:
switch (cj & 3)
{
case 1:
WRITE_REGISTER_UCHAR(pdDst, *(UCHAR*) pdSrc);
MEMORY_BARRIER();
break;
case 2:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
break;
case 3:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
WRITE_REGISTER_UCHAR(pdDst, *((UCHAR*) pdSrc + 2));
MEMORY_BARRIER();
break;
}
IO_WAIT_TRANSFER_DONE(ppdev, pjIoBase);
} while (pgp++, --cGlyph != 0);
}
else
{
////////////////////////////////////////////////////////////
// Mono Spacing
ulCharInc = pstro->ulCharInc;
pgb = pgp->pgdf->pgb;
ptlOrigin.x = pgb->ptlOrigin.x + pgp->ptl.x;
ptlOrigin.y = pgb->ptlOrigin.y + pgp->ptl.y;
pdDst = (ULONG*) pjScreen;
IO_WAIT_FOR_IDLE(ppdev, pjIoBase);
do {
pgb = pgp->pgdf->pgb;
IO_DEST_XY(ppdev, pjIoBase, ptlOrigin.x, ptlOrigin.y);
ptlOrigin.x += ulCharInc;
cxGlyph = pgb->sizlBitmap.cx;
IO_BITMAP_WIDTH(ppdev, pjIoBase, cxGlyph);
cyGlyph = pgb->sizlBitmap.cy;
IO_BITMAP_HEIGHT(ppdev, pjIoBase, cyGlyph);
IO_BLT_CMD_0(ppdev, pjIoBase, START_BLT);
pdSrc = (ULONG*) pgb->aj;
cj = cyGlyph * ((cxGlyph + 7) >> 3);
cd = cj >> 2;
if (cd != 0)
{
do {
WRITE_REGISTER_ULONG(pdDst, *pdSrc);
MEMORY_BARRIER();
pdSrc++;
} while (--cd != 0);
}
// We have to be careful we don't output any more data
// than we're supposed to, otherwise we get garbage on
// the screen:
switch (cj & 3)
{
case 1:
WRITE_REGISTER_UCHAR(pdDst, *(UCHAR*) pdSrc);
MEMORY_BARRIER();
break;
case 2:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
break;
case 3:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
WRITE_REGISTER_UCHAR(pdDst, *((UCHAR*) pdSrc + 2));
MEMORY_BARRIER();
break;
}
IO_WAIT_TRANSFER_DONE(ppdev, pjIoBase);
} while (pgp++, --cGlyph != 0);
}
}
} while (bMoreGlyphs);
}
else
{
// If there's clipping, call off to a function:
vIoClipText(ppdev, pstro, pco);
}
IO_WAIT_FOR_IDLE(ppdev, pjIoBase);
IO_BLT_CONFIG(ppdev, pjIoBase, RESET_BLT);
IO_BLT_CONFIG(ppdev, pjIoBase, BLT_ENABLE);
}
/******************************Public*Routine******************************\
* VOID vMmClipText
*
\**************************************************************************/
VOID vMmClipText(
PDEV* ppdev,
STROBJ* pstro,
CLIPOBJ* pco)
{
BYTE* pjMmBase;
BYTE* pjScreen;
BOOL bMoreGlyphs;
ULONG cGlyphOriginal;
ULONG cGlyph;
GLYPHPOS* pgpOriginal;
GLYPHPOS* pgp;
GLYPHBITS* pgb;
POINTL ptlOrigin;
BOOL bMore;
CLIPENUM ce;
RECTL* prclClip;
ULONG ulCharInc;
LONG cxGlyph;
LONG cyGlyph;
LONG xBias;
LONG cx;
LONG cy;
ULONG* pdSrc;
ULONG* pdDst;
LONG cj;
LONG cd;
LONG xLeft;
LONG yTop;
LONG xRight;
LONG yBottom;
LONG lDelta;
LONG i;
BYTE* pjSrc;
ASSERTDD(pco != NULL, "Don't expect NULL clip objects here");
pjMmBase = ppdev->pjMmBase;
pjScreen = ppdev->pjScreen;
do {
if (pstro->pgp != NULL)
{
// There's only the one batch of glyphs, so save ourselves
// a call:
pgpOriginal = pstro->pgp;
cGlyphOriginal = pstro->cGlyphs;
bMoreGlyphs = FALSE;
}
else
{
bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyphOriginal, &pgpOriginal);
}
if (cGlyphOriginal > 0)
{
ulCharInc = pstro->ulCharInc;
if (pco->iDComplexity == DC_RECT)
{
// We could call 'cEnumStart' and 'bEnum' when the clipping is
// DC_RECT, but the last time I checked, those two calls took
// more than 150 instructions to go through GDI. Since
// 'rclBounds' already contains the DC_RECT clip rectangle,
// and since it's such a common case, we'll special case it:
bMore = FALSE;
prclClip = &pco->rclBounds;
ce.c = 1;
goto SingleRectangle;
}
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
do {
bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (ULONG*) &ce);
for (prclClip = &ce.arcl[0]; ce.c != 0; ce.c--, prclClip++)
{
SingleRectangle:
pgp = pgpOriginal;
cGlyph = cGlyphOriginal;
pgb = pgp->pgdf->pgb;
ptlOrigin.x = pgb->ptlOrigin.x + pgp->ptl.x;
ptlOrigin.y = pgb->ptlOrigin.y + pgp->ptl.y;
pdDst = (ULONG*) pjScreen;
MM_WAIT_FOR_IDLE(ppdev, pjMmBase);
// Loop through all the glyphs for this rectangle:
while (TRUE)
{
cxGlyph = pgb->sizlBitmap.cx;
cyGlyph = pgb->sizlBitmap.cy;
pdSrc = (ULONG*) pgb->aj;
if ((prclClip->left <= ptlOrigin.x) &&
(prclClip->top <= ptlOrigin.y) &&
(prclClip->right >= ptlOrigin.x + cxGlyph) &&
(prclClip->bottom >= ptlOrigin.y + cyGlyph))
{
//-----------------------------------------------------
// Unclipped glyph
MM_DEST_XY(ppdev, pjMmBase, ptlOrigin.x, ptlOrigin.y);
MM_BITMAP_WIDTH(ppdev, pjMmBase, cxGlyph);
MM_BITMAP_HEIGHT(ppdev, pjMmBase, cyGlyph);
MM_BLT_CMD_0(ppdev, pjMmBase, START_BLT);
cj = cyGlyph * ((cxGlyph + 7) >> 3);
cd = cj >> 2;
if (cd != 0)
{
do {
WRITE_REGISTER_ULONG(pdDst, *pdSrc);
MEMORY_BARRIER();
pdSrc++;
} while (--cd != 0);
}
// We have to be careful we don't output any more data
// than we're supposed to, otherwise we get garbage on
// the screen:
switch (cj & 3)
{
case 1:
WRITE_REGISTER_UCHAR(pdDst, *(UCHAR*) pdSrc);
MEMORY_BARRIER();
break;
case 2:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
break;
case 3:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
WRITE_REGISTER_UCHAR(pdDst, *((UCHAR*) pdSrc + 2));
MEMORY_BARRIER();
break;
}
MM_WAIT_TRANSFER_DONE(ppdev, pjMmBase);
}
else
{
//-----------------------------------------------------
// Clipped glyph
// Find the intersection of the glyph rectangle
// and the clip rectangle:
xLeft = max(prclClip->left, ptlOrigin.x);
yTop = max(prclClip->top, ptlOrigin.y);
xRight = min(prclClip->right, ptlOrigin.x + cxGlyph);
yBottom = min(prclClip->bottom, ptlOrigin.y + cyGlyph);
// Check for trivial rejection:
if (((cx = xRight - xLeft) > 0) &&
((cy = yBottom - yTop) > 0))
{
MM_DEST_XY(ppdev, pjMmBase, xLeft, yTop);
MM_BITMAP_WIDTH(ppdev, pjMmBase, cx);
MM_BITMAP_HEIGHT(ppdev, pjMmBase, cy);
xBias = (xLeft - ptlOrigin.x) & 7;
cx += xBias;
MM_SRC_ALIGN(ppdev, pjMmBase, xBias);
lDelta = (cxGlyph + 7) >> 3;
pjSrc = (BYTE*) pdSrc + (yTop - ptlOrigin.y) * lDelta
+ ((xLeft - ptlOrigin.x) >> 3);
cj = (cx + 7) >> 3;
lDelta -= cj;
MM_BLT_CMD_0(ppdev, pjMmBase, START_BLT);
do {
i = cj;
do {
WRITE_REGISTER_UCHAR((UCHAR*) pdDst, *pjSrc);
MEMORY_BARRIER();
pjSrc++;
} while (--i != 0);
pjSrc += lDelta;
} while (--cy != 0);
// Reset the alignment register in case we have more
// unclipped glyphs in this string:
MM_SRC_ALIGN(ppdev, pjMmBase, 0);
MM_WAIT_TRANSFER_DONE(ppdev, pjMmBase);
}
}
if (--cGlyph == 0)
break;
// Get ready for next glyph:
pgp++;
pgb = pgp->pgdf->pgb;
if (ulCharInc == 0)
{
ptlOrigin.x = pgp->ptl.x + pgb->ptlOrigin.x;
ptlOrigin.y = pgp->ptl.y + pgb->ptlOrigin.y;
}
else
{
ptlOrigin.x += ulCharInc;
}
}
}
} while (bMore);
}
} while (bMoreGlyphs);
}
/******************************Public*Routine******************************\
* VOID vMmTextOut
*
\**************************************************************************/
VOID vMmTextOut(
PDEV* ppdev,
STROBJ* pstro,
FONTOBJ* pfo,
CLIPOBJ* pco,
RECTL* prclOpaque,
BRUSHOBJ* pboFore,
BRUSHOBJ* pboOpaque)
{
BYTE* pjMmBase;
BOOL bTextPerfectFit;
ULONG cGlyph;
BOOL bMoreGlyphs;
GLYPHPOS* pgp;
GLYPHBITS* pgb;
LONG cxGlyph;
LONG cyGlyph;
ULONG* pdSrc;
ULONG* pdDst;
BYTE* pjScreen;
LONG cj;
LONG cd;
POINTL ptlOrigin;
LONG ulCharInc;
BYTE iDComplexity;
pjMmBase = ppdev->pjMmBase;
pjScreen = ppdev->pjScreen;
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
if (prclOpaque != NULL)
{
////////////////////////////////////////////////////////////
// Opaque Initialization
////////////////////////////////////////////////////////////
// If we paint the glyphs in 'opaque' mode, we may not actually
// have to draw the opaquing rectangle up-front -- the process
// of laying down all the glyphs will automatically cover all
// of the pixels in the opaquing rectangle.
//
// The condition that must be satisfied is that the text must
// fit 'perfectly' such that the entire background rectangle is
// covered, and none of the glyphs overlap (if the glyphs
// overlap, such as for italics, they have to be drawn in
// transparent mode after the opaquing rectangle is cleared).
bTextPerfectFit = (pstro->flAccel & (SO_ZERO_BEARINGS |
SO_FLAG_DEFAULT_PLACEMENT | SO_MAXEXT_EQUAL_BM_SIDE |
SO_CHAR_INC_EQUAL_BM_BASE)) ==
(SO_ZERO_BEARINGS | SO_FLAG_DEFAULT_PLACEMENT |
SO_MAXEXT_EQUAL_BM_SIDE | SO_CHAR_INC_EQUAL_BM_BASE);
if (!(bTextPerfectFit) ||
(pstro->rclBkGround.top > prclOpaque->top) ||
(pstro->rclBkGround.left > prclOpaque->left) ||
(pstro->rclBkGround.right < prclOpaque->right) ||
(pstro->rclBkGround.bottom < prclOpaque->bottom))
{
if (iDComplexity == DC_TRIVIAL)
{
MM_WAIT_FOR_IDLE(ppdev, pjMmBase);
MM_PREG_COLOR_8(ppdev, pjMmBase, pboOpaque->iSolidColor);
MM_CTRL_REG_1(ppdev, pjMmBase, PACKED_PIXEL_VIEW |
BLOCK_WRITE | // Note
BITS_PER_PIX_8 |
ENAB_TRITON_MODE);
MM_BLT_CMD_1(ppdev, pjMmBase, XY_SRC_ADDR |
XY_DEST_ADDR);
MM_DATAPATH_CTRL(ppdev, pjMmBase, ROPSELECT_NO_ROPS |
PIXELMASK_ONLY |
PLANARMASK_NONE_0XFF |
SRC_IS_PATTERN_REGS);
MM_BITMAP_HEIGHT(ppdev, pjMmBase, prclOpaque->bottom - prclOpaque->top);
MM_BITMAP_WIDTH(ppdev, pjMmBase, prclOpaque->right - prclOpaque->left);
MM_DEST_XY(ppdev, pjMmBase, prclOpaque->left, prclOpaque->top);
MM_BLT_CMD_0(ppdev, pjMmBase, START_BLT);
}
else
{
vClipSolid(ppdev, 1, prclOpaque, pboOpaque->iSolidColor, pco);
}
}
if (bTextPerfectFit)
{
// If we have already drawn the opaquing rectangle (because
// is was larger than the text rectangle), we could lay down
// the glyphs in 'transparent' mode. But I've found the QVision
// to be a bit faster drawing in opaque mode, so we'll stick
// with that:
MM_WAIT_FOR_IDLE(ppdev, pjMmBase);
MM_CTRL_REG_1(ppdev, pjMmBase, EXPAND_TO_FG |
BITS_PER_PIX_8 |
ENAB_TRITON_MODE);
MM_BLT_CMD_1(ppdev, pjMmBase, XY_SRC_ADDR |
XY_DEST_ADDR);
MM_DATAPATH_CTRL(ppdev, pjMmBase, ROPSELECT_NO_ROPS |
PIXELMASK_ONLY |
PLANARMASK_NONE_0XFF |
SRC_IS_CPU_DATA);
MM_FG_COLOR(ppdev, pjMmBase, pboFore->iSolidColor);
MM_BG_COLOR(ppdev, pjMmBase, pboOpaque->iSolidColor);
MM_SRC_ALIGN(ppdev, pjMmBase, 0);
goto SkipTransparentInitialization;
}
}
////////////////////////////////////////////////////////////
// Transparent Initialization
////////////////////////////////////////////////////////////
// Initialize the hardware for transparent text:
MM_WAIT_FOR_IDLE(ppdev, pjMmBase);
MM_CTRL_REG_1(ppdev, pjMmBase, EXPAND_TO_FG |
BITS_PER_PIX_8 |
ENAB_TRITON_MODE);
MM_BLT_CMD_1(ppdev, pjMmBase, XY_SRC_ADDR |
XY_DEST_ADDR);
MM_DATAPATH_CTRL(ppdev, pjMmBase, ROPSELECT_NO_ROPS |
PIXELMASK_AND_SRC_DATA |
PLANARMASK_NONE_0XFF |
SRC_IS_CPU_DATA);
MM_FG_COLOR(ppdev, pjMmBase, pboFore->iSolidColor);
MM_SRC_ALIGN(ppdev, pjMmBase, 0);
SkipTransparentInitialization:
if (iDComplexity == DC_TRIVIAL)
{
do {
if (pstro->pgp != NULL)
{
// There's only the one batch of glyphs, so save ourselves
// a call:
pgp = pstro->pgp;
cGlyph = pstro->cGlyphs;
bMoreGlyphs = FALSE;
}
else
{
bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyph, &pgp);
}
if (cGlyph > 0)
{
if (pstro->ulCharInc == 0)
{
////////////////////////////////////////////////////////////
// Proportional Spacing
pdDst = (ULONG*) pjScreen;
MM_WAIT_FOR_IDLE(ppdev, pjMmBase);
do {
pgb = pgp->pgdf->pgb;
MM_DEST_XY(ppdev, pjMmBase, pgp->ptl.x + pgb->ptlOrigin.x,
pgp->ptl.y + pgb->ptlOrigin.y);
cxGlyph = pgb->sizlBitmap.cx;
MM_BITMAP_WIDTH(ppdev, pjMmBase, cxGlyph);
cyGlyph = pgb->sizlBitmap.cy;
MM_BITMAP_HEIGHT(ppdev, pjMmBase, cyGlyph);
MM_BLT_CMD_0(ppdev, pjMmBase, START_BLT);
pdSrc = (ULONG*) pgb->aj;
cj = cyGlyph * ((cxGlyph + 7) >> 3);
cd = cj >> 2;
if (cd != 0)
{
do {
WRITE_REGISTER_ULONG(pdDst, *pdSrc);
MEMORY_BARRIER();
pdSrc++;
} while (--cd != 0);
}
// We have to be careful we don't output any more data
// than we're supposed to, otherwise we get garbage on
// the screen:
switch (cj & 3)
{
case 1:
WRITE_REGISTER_UCHAR(pdDst, *(UCHAR*) pdSrc);
MEMORY_BARRIER();
break;
case 2:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
break;
case 3:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
WRITE_REGISTER_UCHAR(pdDst, *((UCHAR*) pdSrc + 2));
MEMORY_BARRIER();
break;
}
MM_WAIT_TRANSFER_DONE(ppdev, pjMmBase);
} while (pgp++, --cGlyph != 0);
}
else
{
////////////////////////////////////////////////////////////
// Mono Spacing
ulCharInc = pstro->ulCharInc;
pgb = pgp->pgdf->pgb;
ptlOrigin.x = pgb->ptlOrigin.x + pgp->ptl.x;
ptlOrigin.y = pgb->ptlOrigin.y + pgp->ptl.y;
pdDst = (ULONG*) pjScreen;
MM_WAIT_FOR_IDLE(ppdev, pjMmBase);
do {
pgb = pgp->pgdf->pgb;
MM_DEST_XY(ppdev, pjMmBase, ptlOrigin.x, ptlOrigin.y);
ptlOrigin.x += ulCharInc;
cxGlyph = pgb->sizlBitmap.cx;
MM_BITMAP_WIDTH(ppdev, pjMmBase, cxGlyph);
cyGlyph = pgb->sizlBitmap.cy;
MM_BITMAP_HEIGHT(ppdev, pjMmBase, cyGlyph);
MM_BLT_CMD_0(ppdev, pjMmBase, START_BLT);
pdSrc = (ULONG*) pgb->aj;
cj = cyGlyph * ((cxGlyph + 7) >> 3);
cd = cj >> 2;
if (cd != 0)
{
do {
WRITE_REGISTER_ULONG(pdDst, *pdSrc);
MEMORY_BARRIER();
pdSrc++;
} while (--cd != 0);
}
// We have to be careful we don't output any more data
// than we're supposed to, otherwise we get garbage on
// the screen:
switch (cj & 3)
{
case 1:
WRITE_REGISTER_UCHAR(pdDst, *(UCHAR*) pdSrc);
MEMORY_BARRIER();
break;
case 2:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
break;
case 3:
WRITE_REGISTER_USHORT(pdDst, *(USHORT*) pdSrc);
MEMORY_BARRIER();
WRITE_REGISTER_UCHAR(pdDst, *((UCHAR*) pdSrc + 2));
MEMORY_BARRIER();
break;
}
MM_WAIT_TRANSFER_DONE(ppdev, pjMmBase);
} while (pgp++, --cGlyph != 0);
}
}
} while (bMoreGlyphs);
}
else
{
// If there's clipping, call off to a function:
vMmClipText(ppdev, pstro, pco);
}
}
/******************************Public*Routine******************************\
* BOOL DrvTextOut
*
\**************************************************************************/
BOOL DrvTextOut(
SURFOBJ* pso,
STROBJ* pstro,
FONTOBJ* pfo,
CLIPOBJ* pco,
RECTL* prclExtra, // If we had set GCAPS_HORIZSTRIKE, we would have
// to fill these extra rectangles (it is used
// largely for underlines). It's not a big
// performance win (GDI will call our DrvBitBlt
// to draw the extra rectangles).
RECTL* prclOpaque,
BRUSHOBJ* pboFore,
BRUSHOBJ* pboOpaque,
POINTL* pptlBrush,
MIX mix)
{
PDEV* ppdev;
DSURF* pdsurf;
OH* poh;
pdsurf = (DSURF*) pso->dhsurf;
if (pdsurf->dt != DT_DIB)
{
poh = pdsurf->poh;
ppdev = (PDEV*) pso->dhpdev;
ppdev->xOffset = poh->x;
ppdev->yOffset = poh->y;
// The DDI spec says we'll only ever get foreground and background
// mixes of R2_COPYPEN:
ASSERTDD(mix == 0x0d0d, "GDI should only give us a copy mix");
ppdev->pfnTextOut(ppdev, pstro, pfo, pco, prclOpaque, pboFore, pboOpaque);
}
else
{
// We're drawing to a DFB we've converted to a DIB, so just call GDI
// to handle it:
return(EngTextOut(pdsurf->pso, pstro, pfo, pco, prclExtra, prclOpaque,
pboFore, pboOpaque, pptlBrush, mix));
}
return(TRUE);
}
/******************************Public*Routine******************************\
* VOID DrvDestroyFont
*
* We're being notified that the given font is being deallocated; clean up
* anything we've stashed in the 'pvConsumer' field of the 'pfo'.
*
\**************************************************************************/
VOID DrvDestroyFont(
FONTOBJ* pfo)
{
// !!!
// This call isn't hooked, so GDI will never call it.
//
// This merely exists as a stub function for the sample multi-screen
// support, so that MulDestroyFont can illustrate how multiple screen
// text supports when the driver caches glyphs. If this driver did
// glyph caching, we might have used the 'pvConsumer' field of the
// 'pfo', which we would have to clean up.
}
/******************************Public*Routine******************************\
* BOOL bEnableText
*
* Performs the necessary setup for the text drawing subcomponent.
*
\**************************************************************************/
BOOL bEnableText(
PDEV* ppdev)
{
// Our text algorithms require no initialization. If we were to
// do glyph caching, we would probably want to allocate off-screen
// memory and do a bunch of other stuff here.
return(TRUE);
}
/******************************Public*Routine******************************\
* VOID vDisableText
*
* Performs the necessary clean-up for the text drawing subcomponent.
*
\**************************************************************************/
VOID vDisableText(PDEV* ppdev)
{
// Here we free any stuff allocated in 'bEnableText'.
}
/******************************Public*Routine******************************\
* VOID vAssertModeText
*
* Disables or re-enables the text drawing subcomponent in preparation for
* full-screen entry/exit.
*
\**************************************************************************/
VOID vAssertModeText(
PDEV* ppdev,
BOOL bEnable)
{
// If we were to do off-screen glyph caching, we would probably want
// to invalidate our cache here, because it will get destroyed when
// we switch to full-screen.
}