846 lines
28 KiB
C
846 lines
28 KiB
C
|
/******************************Module*Header*******************************\
|
||
|
* Module Name: textout.c
|
||
|
*
|
||
|
* On every TextOut, GDI provides an array of 'GLYPHPOS' structures
|
||
|
* for every glyph to be drawn. Each GLYPHPOS structure contains a
|
||
|
* glyph handle and a pointer to a monochrome bitmap that describes
|
||
|
* the glyph. (Note that unlike Windows 3.1, which provides a column-
|
||
|
* major glyph bitmap, Windows NT always provides a row-major glyph
|
||
|
* bitmap.) As such, there are three basic methods for drawing text
|
||
|
* with hardware acceleration:
|
||
|
*
|
||
|
* 1) Glyph caching -- Glyph bitmaps are cached by the accelerator
|
||
|
* (probably in off-screen memory), and text is drawn by
|
||
|
* referring the hardware to the cached glyph locations.
|
||
|
*
|
||
|
* 2) Glyph expansion -- Each individual glyph is color-expanded
|
||
|
* directly to the screen from the monochrome glyph bitmap
|
||
|
* supplied by GDI.
|
||
|
*
|
||
|
* 3) Buffer expansion -- The CPU is used to draw all the glyphs into
|
||
|
* a 1bpp monochrome bitmap, and the hardware is then used
|
||
|
* to color-expand the result.
|
||
|
*
|
||
|
* The fastest method depends on a number of variables, such as the
|
||
|
* color expansion speed, bus speed, CPU speed, average glyph size,
|
||
|
* and average string length.
|
||
|
*
|
||
|
* Glyph expansion is typically faster than buffer expansion for very
|
||
|
* large glyphs, even on the ISA bus, because less copying by the CPU
|
||
|
* needs to be done. Unfortunately, large glyphs are pretty rare.
|
||
|
*
|
||
|
* An advantange of the buffer expansion method is that opaque text will
|
||
|
* never flash -- the other two methods typically need to draw the
|
||
|
* opaquing rectangle before laying down the glyphs, which may cause
|
||
|
* a flash if the raster is caught at the wrong time.
|
||
|
*
|
||
|
* This driver implements glyph expansion and buffer expansion --
|
||
|
* methods 2) and 3). Depending on the hardware capabilities at
|
||
|
* run-time, we'll use whichever one will be faster.
|
||
|
*
|
||
|
* Copyright (c) 1992-1995 Microsoft Corporation
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
|
||
|
|
||
|
POINTL gptlZero = { 0, 0 }; // Specifies that the origin of the
|
||
|
// temporary buffer given to the 1bpp
|
||
|
// transfer routine for fasttext is
|
||
|
// at (0, 0)
|
||
|
|
||
|
#define FIFTEEN_BITS ((1 << 15)-1)
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vClipSolid
|
||
|
*
|
||
|
* Fills the specified rectangles with the specified color, honoring
|
||
|
* 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, R4_PATCOPY,
|
||
|
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],
|
||
|
R4_PATCOPY, rbc, NULL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} while (bMore);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL bVerifyStrObj(STROBJ* pstro)
|
||
|
{
|
||
|
BOOL bMoreGlyphs;
|
||
|
LONG cGlyph;
|
||
|
GLYPHPOS * pgp;
|
||
|
LONG iGlyph = 0;
|
||
|
RECTL * prclDraw;
|
||
|
GLYPHPOS * pgpTmp;
|
||
|
POINTL ptlPlace;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Get the next batch of glyphs:
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
prclDraw = &pstro->rclBkGround;
|
||
|
pgpTmp = pgp;
|
||
|
|
||
|
ptlPlace = pgpTmp->ptl;
|
||
|
|
||
|
while (cGlyph)
|
||
|
{
|
||
|
if (((ptlPlace.x + pgpTmp->pgdf->pgb->ptlOrigin.x + pgpTmp->pgdf->pgb->sizlBitmap.cx) > (prclDraw->right)) ||
|
||
|
((ptlPlace.x + pgpTmp->pgdf->pgb->ptlOrigin.x) < (prclDraw->left)) ||
|
||
|
((ptlPlace.y + pgpTmp->pgdf->pgb->ptlOrigin.y + pgpTmp->pgdf->pgb->sizlBitmap.cy) > (prclDraw->bottom)) ||
|
||
|
((ptlPlace.y + pgpTmp->pgdf->pgb->ptlOrigin.y) < (prclDraw->top))
|
||
|
)
|
||
|
{
|
||
|
DISPDBG((0,"------------------------------------------------------------"));
|
||
|
DISPDBG((0,"Glyph %d extends beyond pstro->rclBkGround", iGlyph));
|
||
|
DISPDBG((0,"\tpstro->rclBkGround (%d,%d,%d,%d)",
|
||
|
pstro->rclBkGround.left,
|
||
|
pstro->rclBkGround.top,
|
||
|
pstro->rclBkGround.right,
|
||
|
pstro->rclBkGround.bottom));
|
||
|
DISPDBG((0,"\teffective glyph rect (%d,%d,%d,%d)",
|
||
|
(ptlPlace.x + pgpTmp->pgdf->pgb->ptlOrigin.x),
|
||
|
(ptlPlace.y + pgpTmp->pgdf->pgb->ptlOrigin.y),
|
||
|
(ptlPlace.x + pgpTmp->pgdf->pgb->ptlOrigin.x + pgpTmp->pgdf->pgb->sizlBitmap.cx),
|
||
|
(ptlPlace.y + pgpTmp->pgdf->pgb->ptlOrigin.y + pgpTmp->pgdf->pgb->sizlBitmap.cy)));
|
||
|
DISPDBG((0,"\tglyph pos (%d,%d)",ptlPlace.x,ptlPlace.y));
|
||
|
DISPDBG((0,"\tglyph origin (%d,%d)",
|
||
|
pgpTmp->pgdf->pgb->ptlOrigin.x,
|
||
|
pgpTmp->pgdf->pgb->ptlOrigin.y));
|
||
|
DISPDBG((0,"\tglyph sizl (%d,%d)",
|
||
|
pgpTmp->pgdf->pgb->sizlBitmap.cx,
|
||
|
pgpTmp->pgdf->pgb->sizlBitmap.cy));
|
||
|
DISPDBG((0,"------------------------------------------------------------"));
|
||
|
RIP("time to call the font guys...");
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
cGlyph--;
|
||
|
iGlyph++;
|
||
|
pgpTmp++;
|
||
|
|
||
|
if (pstro->ulCharInc == 0)
|
||
|
{
|
||
|
ptlPlace = pgpTmp->ptl;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ptlPlace.x += pstro->ulCharInc;
|
||
|
}
|
||
|
}
|
||
|
} while (bMoreGlyphs);
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* BOOL bBufferExpansion
|
||
|
*
|
||
|
* Outputs text using the 'buffer expansion' method. The CPU draws to a
|
||
|
* 1bpp buffer, and the result is color-expanded to the screen using the
|
||
|
* hardware.
|
||
|
*
|
||
|
* Note that this is x86 only ('vFastText', which draws the glyphs to the
|
||
|
* 1bpp buffer, is writen in Asm).
|
||
|
*
|
||
|
* If you're just getting your driver working, this is the fastest way to
|
||
|
* bring up working accelerated text. All you have to do is write the
|
||
|
* 'Xfer1bpp' function that's also used by the blt code. This
|
||
|
* 'bBufferExpansion' routine shouldn't need to be modified at all.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
#if defined(i386)
|
||
|
|
||
|
BOOL bBufferExpansion(
|
||
|
PDEV* ppdev,
|
||
|
STROBJ* pstro,
|
||
|
CLIPOBJ* pco,
|
||
|
RECTL* prclExtra,
|
||
|
RECTL* prclOpaque,
|
||
|
BRUSHOBJ* pboFore,
|
||
|
BRUSHOBJ* pboOpaque)
|
||
|
{
|
||
|
BYTE jClip;
|
||
|
BOOL bMore; // Flag for clip enumeration
|
||
|
GLYPHPOS* pgp; // Points to the first glyph
|
||
|
BOOL bMoreGlyphs; // Glyph enumeration flag
|
||
|
ULONG cGlyph; // # of glyphs in one batch
|
||
|
RECTL arclTmp[4]; // Temporary storage for portions
|
||
|
// of opaquing rectangle
|
||
|
RECTL* prclClip; // Points to list of clip rectangles
|
||
|
RECTL* prclDraw; // Actual text to be drawn
|
||
|
RECTL rclDraw;
|
||
|
ULONG crcl; // Temporary rectangle count
|
||
|
ULONG ulBufferBytes;
|
||
|
ULONG ulBufferHeight;
|
||
|
BOOL bTextPerfectFit;
|
||
|
ULONG flDraw;
|
||
|
BOOL bTmpAlloc;
|
||
|
SURFOBJ so;
|
||
|
CLIPENUM ce;
|
||
|
RBRUSH_COLOR rbc;
|
||
|
ROP4 rop4MonoExpand; // Dictates whether opaque or
|
||
|
// transparent text
|
||
|
XLATEOBJ xlo; // Temporary for passing colors
|
||
|
XLATECOLORS xlc; // Temporary for keeping colors
|
||
|
|
||
|
jClip = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
|
||
|
|
||
|
// The foreground color will always be solid:
|
||
|
|
||
|
xlc.iForeColor = pboFore->iSolidColor;
|
||
|
|
||
|
ASSERTDD(xlc.iForeColor != -1, "Expected solid foreground color");
|
||
|
|
||
|
// See if the temporary buffer is big enough for the text; if
|
||
|
// not, try to allocate enough memory. We round up to the
|
||
|
// nearest dword multiple:
|
||
|
|
||
|
so.lDelta = ((((pstro->rclBkGround.right + 31) & ~31) -
|
||
|
(pstro->rclBkGround.left & ~31)) >> 3);
|
||
|
|
||
|
ulBufferHeight = pstro->rclBkGround.bottom - pstro->rclBkGround.top;
|
||
|
|
||
|
ulBufferBytes = so.lDelta * ulBufferHeight;
|
||
|
|
||
|
if (((ULONG)so.lDelta > FIFTEEN_BITS) ||
|
||
|
(ulBufferHeight > FIFTEEN_BITS))
|
||
|
{
|
||
|
// the math will have overflowed
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
// Use our temporary buffer if it's big enough, otherwise
|
||
|
// allocate a buffer on the fly:
|
||
|
|
||
|
if (ulBufferBytes >= TMP_BUFFER_SIZE)
|
||
|
{
|
||
|
// The textout is so big that I doubt this allocation will
|
||
|
// cost a significant amount in performance:
|
||
|
|
||
|
bTmpAlloc = TRUE;
|
||
|
so.pvScan0 = EngAllocUserMem(ulBufferBytes, ALLOC_TAG);
|
||
|
if (so.pvScan0 == NULL)
|
||
|
return(FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bTmpAlloc = FALSE;
|
||
|
so.pvScan0 = ppdev->pvTmpBuffer;
|
||
|
}
|
||
|
|
||
|
// Set fixed pitch, overlap, and top and bottom 'y' alignment
|
||
|
// flags:
|
||
|
|
||
|
if (!(pstro->flAccel & SO_HORIZONTAL) ||
|
||
|
(pstro->flAccel & SO_REVERSED))
|
||
|
{
|
||
|
flDraw = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
flDraw = ((pstro->ulCharInc != 0) ? 0x01 : 0) |
|
||
|
(((pstro->flAccel & (SO_ZERO_BEARINGS |
|
||
|
SO_FLAG_DEFAULT_PLACEMENT)) !=
|
||
|
(SO_ZERO_BEARINGS | SO_FLAG_DEFAULT_PLACEMENT))
|
||
|
? 0x02 : 0) |
|
||
|
(((pstro->flAccel & (SO_ZERO_BEARINGS |
|
||
|
SO_FLAG_DEFAULT_PLACEMENT |
|
||
|
SO_MAXEXT_EQUAL_BM_SIDE)) ==
|
||
|
(SO_ZERO_BEARINGS | SO_FLAG_DEFAULT_PLACEMENT |
|
||
|
SO_MAXEXT_EQUAL_BM_SIDE)) ? 0x04 : 0);
|
||
|
}
|
||
|
|
||
|
// If there's an opaque rectangle, we'll do as much opaquing
|
||
|
// as possible as we do the text. If the opaque rectangle is
|
||
|
// larger than the text rectangle, then we'll do the fringe
|
||
|
// areas right now, and the text and associated background
|
||
|
// areas together later:
|
||
|
|
||
|
rop4MonoExpand = R4_XPAR_EXPAND;
|
||
|
|
||
|
DISPDBG((11,"[%d] rop4MonoExpand(%04x)", __LINE__, rop4MonoExpand));
|
||
|
|
||
|
if (prclOpaque != NULL)
|
||
|
{
|
||
|
rop4MonoExpand = R4_SRCCOPY;
|
||
|
DISPDBG((11,"[%d] rop4MonoExpand(%04x)", __LINE__, rop4MonoExpand));
|
||
|
|
||
|
// Since we didn't set GCAPS_ARBRUSHOPAQUE (yes, it's
|
||
|
// missing a 'b'), we don't have to worry about getting
|
||
|
// anything other than a solid opaquing brush. I wouldn't
|
||
|
// recommend handling it anyway, since I'll bet it would
|
||
|
// break quite a few applications:
|
||
|
|
||
|
xlc.iBackColor = pboOpaque->iSolidColor;
|
||
|
|
||
|
ASSERTDD(xlc.iBackColor != -1, "Expected solid background color");
|
||
|
|
||
|
// See if we have fringe areas to do. If so, build a list of
|
||
|
// rectangles to fill, in right-down order:
|
||
|
|
||
|
crcl = 0;
|
||
|
|
||
|
// Top fragment:
|
||
|
|
||
|
if (pstro->rclBkGround.top > prclOpaque->top)
|
||
|
{
|
||
|
arclTmp[crcl].top = prclOpaque->top;
|
||
|
arclTmp[crcl].left = prclOpaque->left;
|
||
|
arclTmp[crcl].right = prclOpaque->right;
|
||
|
arclTmp[crcl++].bottom = pstro->rclBkGround.top;
|
||
|
}
|
||
|
|
||
|
// Left fragment:
|
||
|
|
||
|
if (pstro->rclBkGround.left > prclOpaque->left)
|
||
|
{
|
||
|
arclTmp[crcl].top = pstro->rclBkGround.top;
|
||
|
arclTmp[crcl].left = prclOpaque->left;
|
||
|
arclTmp[crcl].right = pstro->rclBkGround.left;
|
||
|
arclTmp[crcl++].bottom = pstro->rclBkGround.bottom;
|
||
|
}
|
||
|
|
||
|
// Right fragment:
|
||
|
|
||
|
if (pstro->rclBkGround.right < prclOpaque->right)
|
||
|
{
|
||
|
arclTmp[crcl].top = pstro->rclBkGround.top;
|
||
|
arclTmp[crcl].right = prclOpaque->right;
|
||
|
arclTmp[crcl].left = pstro->rclBkGround.right;
|
||
|
arclTmp[crcl++].bottom = pstro->rclBkGround.bottom;
|
||
|
}
|
||
|
|
||
|
// Bottom fragment:
|
||
|
|
||
|
if (pstro->rclBkGround.bottom < prclOpaque->bottom)
|
||
|
{
|
||
|
arclTmp[crcl].bottom = prclOpaque->bottom;
|
||
|
arclTmp[crcl].left = prclOpaque->left;
|
||
|
arclTmp[crcl].right = prclOpaque->right;
|
||
|
arclTmp[crcl++].top = pstro->rclBkGround.bottom;
|
||
|
}
|
||
|
|
||
|
// Fill any fringe rectangles we found:
|
||
|
|
||
|
if (crcl != 0)
|
||
|
{
|
||
|
if (jClip == DC_TRIVIAL)
|
||
|
{
|
||
|
rbc.iSolidColor = xlc.iBackColor;
|
||
|
(ppdev->pfnFillSolid)(ppdev, crcl, arclTmp, R4_PATCOPY, rbc, NULL);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vClipSolid(ppdev, crcl, arclTmp, xlc.iBackColor, pco);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We're done with separate opaquing; any further opaquing will
|
||
|
// happen as part of the text drawing.
|
||
|
|
||
|
// Clear the buffer if the text isn't going to set every bit:
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
// Note that we already rounded up to a dword multiple size.
|
||
|
|
||
|
vClearMemDword((ULONG*) so.pvScan0, ulBufferBytes >> 2);
|
||
|
}
|
||
|
|
||
|
// Fake up the translate object that will provide the 1bpp
|
||
|
// transfer routine the foreground and background colors:
|
||
|
|
||
|
xlo.pulXlate = (ULONG*) &xlc;
|
||
|
|
||
|
// Draw the text into the temp buffer, and thence to the screen:
|
||
|
|
||
|
#if DBG
|
||
|
if (!bVerifyStrObj(pstro))
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
STROBJ_vEnumStart(pstro);
|
||
|
#endif
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Get the next batch of glyphs:
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
// LATER: remove double clip intersection from ASM code
|
||
|
|
||
|
if (cGlyph)
|
||
|
{
|
||
|
prclClip = NULL;
|
||
|
prclDraw = &pstro->rclBkGround;
|
||
|
|
||
|
if (jClip == DC_TRIVIAL)
|
||
|
{
|
||
|
|
||
|
Output_Text:
|
||
|
|
||
|
vFastText(pgp,
|
||
|
cGlyph,
|
||
|
so.pvScan0,
|
||
|
so.lDelta,
|
||
|
pstro->ulCharInc,
|
||
|
&pstro->rclBkGround,
|
||
|
prclOpaque,
|
||
|
flDraw,
|
||
|
prclClip,
|
||
|
prclExtra);
|
||
|
|
||
|
if (!bMoreGlyphs)
|
||
|
{
|
||
|
DISPDBG((11,"[%d] rop4MonoExpand(%04x)", __LINE__, rop4MonoExpand));
|
||
|
(ppdev->pfnXfer1bpp)(ppdev,
|
||
|
1,
|
||
|
prclDraw,
|
||
|
rop4MonoExpand,
|
||
|
&so,
|
||
|
&gptlZero,
|
||
|
&pstro->rclBkGround,
|
||
|
&xlo);
|
||
|
}
|
||
|
}
|
||
|
else if (jClip == DC_RECT)
|
||
|
{
|
||
|
if (bIntersect(&pco->rclBounds, &pstro->rclBkGround,
|
||
|
&rclDraw))
|
||
|
{
|
||
|
DISPDBG((11,"text was DC_RECT clipping"));
|
||
|
arclTmp[0] = pco->rclBounds;
|
||
|
arclTmp[1].bottom = 0; // Terminate list
|
||
|
prclClip = &arclTmp[0];
|
||
|
prclDraw = &rclDraw;
|
||
|
|
||
|
// Save some code size by jumping to the common
|
||
|
// functions calls:
|
||
|
|
||
|
goto Output_Text;
|
||
|
}
|
||
|
}
|
||
|
else // jClip == DC_COMPLEX
|
||
|
{
|
||
|
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES,
|
||
|
CD_ANY, 0);
|
||
|
|
||
|
do
|
||
|
{
|
||
|
bMore = CLIPOBJ_bEnum(pco,
|
||
|
sizeof(ce) - sizeof(RECTL),
|
||
|
(ULONG*) &ce);
|
||
|
|
||
|
ce.c = cIntersect(&pstro->rclBkGround,
|
||
|
ce.arcl, ce.c);
|
||
|
|
||
|
if (ce.c != 0)
|
||
|
{
|
||
|
ce.arcl[ce.c].bottom = 0; // Terminate list
|
||
|
|
||
|
vFastText(pgp,
|
||
|
cGlyph,
|
||
|
so.pvScan0,
|
||
|
so.lDelta,
|
||
|
pstro->ulCharInc,
|
||
|
&pstro->rclBkGround,
|
||
|
prclOpaque,
|
||
|
flDraw,
|
||
|
&ce.arcl[0],
|
||
|
prclExtra);
|
||
|
|
||
|
if (!bMoreGlyphs)
|
||
|
{
|
||
|
DISPDBG((11,"[%d] rop4MonoExpand(%04x)", __LINE__, rop4MonoExpand));
|
||
|
(ppdev->pfnXfer1bpp)(ppdev,
|
||
|
ce.c,
|
||
|
&ce.arcl[0],
|
||
|
rop4MonoExpand,
|
||
|
&so,
|
||
|
&gptlZero,
|
||
|
&pstro->rclBkGround,
|
||
|
&xlo);
|
||
|
}
|
||
|
}
|
||
|
} while (bMore);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} while (bMoreGlyphs);
|
||
|
|
||
|
// Free up any memory we allocated for the temp buffer:
|
||
|
|
||
|
if (bTmpAlloc)
|
||
|
{
|
||
|
EngFreeUserMem(so.pvScan0);
|
||
|
}
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
#endif // defined(i386)
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* BOOL DrvTextOut
|
||
|
*
|
||
|
* If it's the fastest method, outputs text using the 'glyph expansion'
|
||
|
* method. Each individual glyph is color-expanded directly to the
|
||
|
* screen from the monochrome glyph bitmap supplied by GDI.
|
||
|
*
|
||
|
* If it's not the fastest method, calls the routine that implements the
|
||
|
* 'buffer expansion' method.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
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;
|
||
|
BOOL bTextPerfectFit;
|
||
|
ULONG cGlyph;
|
||
|
BOOL bMoreGlyphs;
|
||
|
GLYPHPOS* pgp;
|
||
|
GLYPHBITS* pgb;
|
||
|
BYTE* pjGlyph;
|
||
|
LONG cyGlyph;
|
||
|
POINTL ptlOrigin;
|
||
|
LONG ulCharInc;
|
||
|
BYTE iDComplexity;
|
||
|
LONG lDelta;
|
||
|
LONG cw;
|
||
|
|
||
|
// 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");
|
||
|
|
||
|
// Pass the surface off to GDI if it's a device bitmap that we've
|
||
|
// converted to a DIB:
|
||
|
|
||
|
pdsurf = (DSURF*) pso->dhsurf;
|
||
|
|
||
|
if (pdsurf->dt != DT_DIB)
|
||
|
{
|
||
|
// We'll be drawing to the screen or an off-screen DFB; copy the
|
||
|
// surface's offset now so that we won't need to refer to the DSURF
|
||
|
// again:
|
||
|
|
||
|
poh = pdsurf->poh;
|
||
|
ppdev = (PDEV*) pso->dhpdev;
|
||
|
|
||
|
ppdev->xOffset = poh->x;
|
||
|
ppdev->yOffset = poh->y;
|
||
|
ppdev->xyOffset = (poh->x * ppdev->cBpp) +
|
||
|
(poh->y * ppdev->lDelta);
|
||
|
|
||
|
if (ppdev->bAutoBanking)
|
||
|
{
|
||
|
PVOID pvScan0;
|
||
|
BOOL bRet;
|
||
|
BYTE* pjBase = ppdev->pjBase;
|
||
|
|
||
|
pvScan0 = ppdev->psoFrameBuffer->pvScan0;
|
||
|
|
||
|
(BYTE*)ppdev->psoFrameBuffer->pvScan0 += ppdev->xyOffset;
|
||
|
|
||
|
WAIT_FOR_IDLE_ACL(ppdev, pjBase);
|
||
|
bRet = EngTextOut(ppdev->psoFrameBuffer, pstro, pfo, pco, prclExtra,
|
||
|
prclOpaque, pboFore, pboOpaque, pptlBrush, mix);
|
||
|
|
||
|
ppdev->psoFrameBuffer->pvScan0 = pvScan0;
|
||
|
return(bRet);
|
||
|
}
|
||
|
|
||
|
|
||
|
{
|
||
|
#if defined(i386)
|
||
|
{
|
||
|
// We don't want to use the 'glyph expansion' method, so use
|
||
|
// the 'buffer expansion' method instead:
|
||
|
|
||
|
return(bBufferExpansion(ppdev, pstro, pco, prclExtra, prclOpaque,
|
||
|
pboFore, pboOpaque));
|
||
|
}
|
||
|
#else
|
||
|
{
|
||
|
BANK bnk;
|
||
|
BOOL b;
|
||
|
RECTL rclDraw;
|
||
|
RECTL *prclDst = &pco->rclBounds;
|
||
|
|
||
|
// The bank manager requires that the 'draw' rectangle be
|
||
|
// well-ordered:
|
||
|
|
||
|
rclDraw = *prclDst;
|
||
|
if (rclDraw.left > rclDraw.right)
|
||
|
{
|
||
|
rclDraw.left = prclDst->right;
|
||
|
rclDraw.right = prclDst->left;
|
||
|
}
|
||
|
if (rclDraw.top > rclDraw.bottom)
|
||
|
{
|
||
|
rclDraw.top = prclDst->bottom;
|
||
|
rclDraw.bottom = prclDst->top;
|
||
|
}
|
||
|
|
||
|
vBankStart(ppdev, &rclDraw, pco, &bnk);
|
||
|
|
||
|
b = TRUE;
|
||
|
do {
|
||
|
b &= EngTextOut(bnk.pso,
|
||
|
pstro,
|
||
|
pfo,
|
||
|
bnk.pco,
|
||
|
prclExtra,
|
||
|
prclOpaque,
|
||
|
pboFore,
|
||
|
pboOpaque,
|
||
|
pptlBrush,
|
||
|
mix);
|
||
|
} while (bBankEnum(&bnk));
|
||
|
|
||
|
return(b);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
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******************************\
|
||
|
* 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.
|
||
|
}
|
||
|
|
||
|
/******************************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.
|
||
|
}
|