2702 lines
81 KiB
C
2702 lines
81 KiB
C
/******************************Module*Header*******************************\
|
|
*
|
|
* *******************
|
|
* * GDI SAMPLE CODE *
|
|
* *******************
|
|
*
|
|
* 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 colour-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 colour-expand the result.
|
|
*
|
|
* In addition, 2) and 3) can each have two permutations:
|
|
*
|
|
* a) Glyphs are bit-packed -- The fastest method, where no bits
|
|
* are used as padding between scans of the glyph.
|
|
*
|
|
* b) Glyphs are byte-, word-, or dword-packed -- The slower method,
|
|
* where the hardware requires that each scan be padded with
|
|
* unused bits to fill out to the end of the byte, word, or
|
|
* dword.
|
|
*
|
|
* The fastest method depends on a number of variables, such as the
|
|
* colour expansion speed, bus speed, CPU speed, average glyph size,
|
|
* and average string length.
|
|
*
|
|
* For the S3 with normal sized glyphs, I've found that caching the
|
|
* glyphs in off-screen memory is typically the slowest method.
|
|
* Buffer expansion is typically fastest on the slow ISA bus (or when
|
|
* memory-mapped I/O isn't available on the x86), and glyph expansion
|
|
* is best on fast buses such as VL and PCI.
|
|
*
|
|
* 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-1998 Microsoft Corporation
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
RECTL grclMax = { 0, 0, 0x8000, 0x8000 };
|
|
// Maximal clip rectangle for trivial clipping
|
|
|
|
BYTE gajBit[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
|
|
// Converts bit index to set bit
|
|
|
|
#define FIFTEEN_BITS ((1 << 15)-1)
|
|
|
|
/******************************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******************************\
|
|
* BOOL bIoTextOut
|
|
*
|
|
* Outputs text using the 'buffer expansion' method. We call GDI to draw
|
|
* all the glyphs to a single monochrome buffer, and then we use the
|
|
* hardware to colour expand the result to the screen.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bIoTextOut(
|
|
SURFOBJ* pso,
|
|
STROBJ* pstro,
|
|
FONTOBJ* pfo,
|
|
CLIPOBJ* pco,
|
|
RECTL* prclOpaque,
|
|
BRUSHOBJ* pboFore,
|
|
BRUSHOBJ* pboOpaque)
|
|
{
|
|
PDEV* ppdev;
|
|
RECTL* prclBounds;
|
|
LONG lDelta;
|
|
ULONG ulBufferHeight;
|
|
ULONG ulBufferBytes;
|
|
BOOL bTmpAlloc;
|
|
VOID* pvTmp;
|
|
SURFOBJ* psoTmp;
|
|
BOOL bOpaque;
|
|
BRUSHOBJ boFore;
|
|
BRUSHOBJ boOpaque;
|
|
BOOL bRet;
|
|
XLATECOLORS xlc; // Temporary for keeping colours
|
|
XLATEOBJ xlo; // Temporary for passing colours
|
|
CLIPENUM ce; // Clip enumeration object
|
|
RBRUSH_COLOR rbc;
|
|
RECTL* prclClip;
|
|
RECTL rclClip;
|
|
BOOL bMore;
|
|
ROP4 rop4;
|
|
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
|
|
// If asked to do an opaque TextOut, we'll set it up so that the
|
|
// 1bpp blt we do will automatically opaque the 'rclBkGround'
|
|
// rectangle. But we have to handle here the case if 'prclOpaque'
|
|
// is bigger than 'rclBkGround':
|
|
|
|
if ((prclOpaque != NULL) &&
|
|
((prclOpaque->left != pstro->rclBkGround.left) ||
|
|
(prclOpaque->top != pstro->rclBkGround.top) ||
|
|
(prclOpaque->right != pstro->rclBkGround.right) ||
|
|
(prclOpaque->bottom != pstro->rclBkGround.bottom)))
|
|
{
|
|
rbc.iSolidColor = pboOpaque->iSolidColor;
|
|
prclClip = prclOpaque;
|
|
|
|
if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
|
|
{
|
|
|
|
Output_Opaque:
|
|
|
|
ppdev->pfnFillSolid(ppdev,
|
|
1,
|
|
prclClip,
|
|
0xf0f0,
|
|
rbc,
|
|
NULL);
|
|
}
|
|
else if (pco->iDComplexity == DC_RECT)
|
|
{
|
|
if (bIntersect(&pco->rclBounds, prclOpaque, &rclClip))
|
|
{
|
|
prclClip = &rclClip;
|
|
|
|
// Save some code size by jumping to the common
|
|
// functions calls:
|
|
|
|
goto Output_Opaque;
|
|
}
|
|
}
|
|
else // pco->iDComplexity == 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(prclOpaque, ce.arcl, ce.c);
|
|
|
|
if (ce.c != 0)
|
|
{
|
|
ppdev->pfnFillSolid(ppdev,
|
|
ce.c,
|
|
&ce.arcl[0],
|
|
0xf0f0,
|
|
rbc,
|
|
NULL);
|
|
}
|
|
} while (bMore);
|
|
}
|
|
}
|
|
|
|
// If there is an opaque rectangle then it will be bigger than the
|
|
// background rectangle. We want to test with whichever is bigger.
|
|
|
|
prclBounds = prclOpaque;
|
|
if (prclBounds == NULL)
|
|
{
|
|
prclBounds = &pstro->rclBkGround;
|
|
}
|
|
|
|
if ((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL))
|
|
{
|
|
// I'm not entirely sure why, but GDI will occasionally send
|
|
// us TextOut's where the opaquing rectangle does not intersect
|
|
// with the clip object bounds -- meaning that the text out
|
|
// should have already been trivially rejected. We will do so
|
|
// here because the blt code usually assumes that all trivial
|
|
// rejections will have already been performed, and we will be
|
|
// passing this call on to the blt code:
|
|
|
|
if ((pco->rclBounds.top >= pstro->rclBkGround.bottom) ||
|
|
(pco->rclBounds.left >= pstro->rclBkGround.right) ||
|
|
(pco->rclBounds.right <= pstro->rclBkGround.left) ||
|
|
(pco->rclBounds.bottom <= pstro->rclBkGround.top))
|
|
{
|
|
// The entire operation was trivially rejected:
|
|
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
// 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:
|
|
|
|
lDelta = ((((pstro->rclBkGround.right + 31) & ~31) -
|
|
(pstro->rclBkGround.left & ~31)) >> 3);
|
|
|
|
ulBufferHeight = pstro->rclBkGround.bottom - pstro->rclBkGround.top;
|
|
ulBufferBytes = lDelta * ulBufferHeight;
|
|
|
|
if (((ULONG) lDelta > FIFTEEN_BITS) ||
|
|
(ulBufferHeight > FIFTEEN_BITS))
|
|
{
|
|
// Fail if 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;
|
|
pvTmp = EngAllocUserMem(ulBufferBytes, ALLOC_TAG);
|
|
if (pvTmp == NULL)
|
|
return(FALSE);
|
|
}
|
|
else
|
|
{
|
|
bTmpAlloc = FALSE;
|
|
pvTmp = ppdev->pvTmpBuffer;
|
|
}
|
|
|
|
psoTmp = ppdev->psoText;
|
|
|
|
// Adjust 'lDelta' and 'pvScan0' of our temporary 1bpp surface object
|
|
// so that when GDI starts drawing the text, it will begin in the
|
|
// first dword
|
|
|
|
psoTmp->pvScan0 = (BYTE*) pvTmp - (pstro->rclBkGround.top * lDelta)
|
|
- ((pstro->rclBkGround.left & ~31) >> 3);
|
|
psoTmp->lDelta = lDelta;
|
|
|
|
ASSERTDD(((ULONG_PTR)psoTmp->pvScan0 &3)==0,"pvScan0 must be dword aligned");
|
|
ASSERTDD((lDelta & 3) == 0, "lDelta must be dword aligned");
|
|
|
|
// Get GDI to draw the text for us into a 1bpp buffer:
|
|
|
|
boFore.iSolidColor = 1;
|
|
boOpaque.iSolidColor = 0;
|
|
|
|
bRet = EngTextOut(psoTmp,
|
|
pstro,
|
|
pfo,
|
|
pco,
|
|
NULL,
|
|
&pstro->rclBkGround,
|
|
&boFore,
|
|
&boOpaque,
|
|
NULL,
|
|
0x0d0d);
|
|
|
|
if (bRet)
|
|
{
|
|
// Transparently blt the 1bpp buffer to the screen:
|
|
|
|
xlc.iForeColor = pboFore->iSolidColor;
|
|
xlc.iBackColor = pboOpaque->iSolidColor;
|
|
xlo.pulXlate = (ULONG*) &xlc;
|
|
prclClip = &pstro->rclBkGround;
|
|
|
|
// Rop 'aacc' works out to a transparent blt, while 'cccc' works
|
|
// out to an opaque blt:
|
|
|
|
rop4 = (prclOpaque != NULL) ? 0xcccc : 0xaacc;
|
|
|
|
if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
|
|
{
|
|
|
|
Output_Text:
|
|
|
|
ppdev->pfnXfer1bpp(ppdev,
|
|
1,
|
|
prclClip,
|
|
rop4,
|
|
psoTmp,
|
|
(POINTL*) &pstro->rclBkGround,
|
|
&pstro->rclBkGround,
|
|
&xlo);
|
|
}
|
|
else if (pco->iDComplexity == DC_RECT)
|
|
{
|
|
if (bIntersect(&pco->rclBounds, &pstro->rclBkGround, &rclClip))
|
|
{
|
|
prclClip = &rclClip;
|
|
|
|
// Save some code size by jumping to the common
|
|
// functions calls:
|
|
|
|
goto Output_Text;
|
|
}
|
|
}
|
|
else // pco->iDComplexity == 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)
|
|
{
|
|
ppdev->pfnXfer1bpp(ppdev,
|
|
ce.c,
|
|
&ce.arcl[0],
|
|
rop4,
|
|
psoTmp,
|
|
(POINTL*) &pstro->rclBkGround,
|
|
&pstro->rclBkGround,
|
|
&xlo);
|
|
}
|
|
} while (bMore);
|
|
}
|
|
}
|
|
|
|
// Free up any memory we allocated for the temp buffer:
|
|
|
|
if (bTmpAlloc)
|
|
{
|
|
EngFreeUserMem(pvTmp);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vMmGeneralText
|
|
*
|
|
* Handles any strings that need to be clipped, using the 'glyph
|
|
* expansion' method.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vMmGeneralText(
|
|
PDEV* ppdev,
|
|
STROBJ* pstro,
|
|
CLIPOBJ* pco)
|
|
{
|
|
BYTE* pjMmBase;
|
|
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;
|
|
BYTE* pjGlyph;
|
|
LONG cj;
|
|
LONG cw;
|
|
LONG xLeft;
|
|
LONG yTop;
|
|
LONG xRight;
|
|
LONG yBottom;
|
|
LONG xBias;
|
|
LONG lDelta;
|
|
LONG cx;
|
|
LONG cy;
|
|
BYTE iDComplexity;
|
|
|
|
ASSERTDD(pco != NULL, "Don't expect NULL clip objects here");
|
|
|
|
pjMmBase = ppdev->pjMmBase;
|
|
|
|
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;
|
|
|
|
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
|
|
|
|
if (iDComplexity != DC_COMPLEX)
|
|
{
|
|
// 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;
|
|
ce.c = 1;
|
|
|
|
if (iDComplexity == DC_TRIVIAL)
|
|
prclClip = &grclMax;
|
|
else
|
|
prclClip = &pco->rclBounds;
|
|
|
|
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;
|
|
|
|
// Loop through all the glyphs for this rectangle:
|
|
|
|
while (TRUE)
|
|
{
|
|
cxGlyph = pgb->sizlBitmap.cx;
|
|
cyGlyph = pgb->sizlBitmap.cy;
|
|
|
|
pjGlyph = pgb->aj;
|
|
|
|
if ((prclClip->left <= ptlOrigin.x) &&
|
|
(prclClip->top <= ptlOrigin.y) &&
|
|
(prclClip->right >= ptlOrigin.x + cxGlyph) &&
|
|
(prclClip->bottom >= ptlOrigin.y + cyGlyph))
|
|
{
|
|
//-----------------------------------------------------
|
|
// Unclipped glyph
|
|
|
|
IO_FIFO_WAIT(ppdev, 4);
|
|
|
|
MM_CUR_X(ppdev, pjMmBase, ptlOrigin.x);
|
|
MM_CUR_Y(ppdev, pjMmBase, ptlOrigin.y);
|
|
MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, cxGlyph - 1);
|
|
MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cyGlyph - 1);
|
|
|
|
IO_GP_WAIT(ppdev);
|
|
|
|
if (cxGlyph <= 8)
|
|
{
|
|
//-----------------------------------------------------
|
|
// 1 to 8 pels in width
|
|
|
|
MM_CMD(ppdev, pjMmBase,
|
|
(RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
|
|
DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE | BYTE_SWAP | BUS_SIZE_16));
|
|
|
|
CHECK_DATA_READY(ppdev);
|
|
|
|
MM_TRANSFER_BYTE_THIN(ppdev, pjMmBase, pjGlyph, cyGlyph);
|
|
|
|
CHECK_DATA_COMPLETE(ppdev);
|
|
}
|
|
else if (cxGlyph <= 16)
|
|
{
|
|
//-----------------------------------------------------
|
|
// 9 to 16 pels in width
|
|
|
|
MM_CMD(ppdev, pjMmBase,
|
|
(RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
|
|
DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE | BYTE_SWAP | BUS_SIZE_16));
|
|
|
|
CHECK_DATA_READY(ppdev);
|
|
|
|
MM_TRANSFER_WORD_ALIGNED(ppdev, pjMmBase, pjGlyph, cyGlyph);
|
|
|
|
CHECK_DATA_COMPLETE(ppdev);
|
|
}
|
|
else
|
|
{
|
|
lDelta = (cxGlyph + 7) >> 3;
|
|
|
|
if (!(lDelta & 1))
|
|
{
|
|
//-----------------------------------------------------
|
|
// Even number of bytes in width
|
|
|
|
MM_CMD(ppdev, pjMmBase,
|
|
(RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
|
|
DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE | BYTE_SWAP | BUS_SIZE_16));
|
|
|
|
CHECK_DATA_READY(ppdev);
|
|
|
|
MM_TRANSFER_WORD_ALIGNED(ppdev, pjMmBase, pjGlyph,
|
|
((lDelta * cyGlyph) >> 1));
|
|
|
|
CHECK_DATA_COMPLETE(ppdev);
|
|
}
|
|
else
|
|
{
|
|
//-----------------------------------------------------
|
|
// Odd number of bytes in width
|
|
|
|
// We revert to byte transfers instead of word transfers
|
|
// because word transfers would cause us to do unaligned
|
|
// reads for every second scan, which could cause us to
|
|
// read past the end of the glyph bitmap, and access
|
|
// violate.
|
|
|
|
MM_CMD(ppdev, pjMmBase,
|
|
(RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
|
|
DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE | BYTE_SWAP | BUS_SIZE_16));
|
|
|
|
CHECK_DATA_READY(ppdev);
|
|
|
|
MM_TRANSFER_WORD_ODD(ppdev, pjMmBase, pjGlyph, lDelta,
|
|
cyGlyph);
|
|
|
|
CHECK_DATA_COMPLETE(ppdev);
|
|
}
|
|
}
|
|
}
|
|
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_FIFO_WAIT(ppdev, 5);
|
|
|
|
xBias = (xLeft - ptlOrigin.x) & 7;
|
|
if (xBias != 0)
|
|
{
|
|
// 'xBias' is the bit position in the monochrome glyph
|
|
// bitmap of the first pixel to be lit, relative to
|
|
// the start of the byte. That is, if 'xBias' is 2,
|
|
// then the first unclipped pixel is represented by bit
|
|
// 2 of the corresponding bitmap byte.
|
|
//
|
|
// Normally, the accelerator expects bit 0 to be the
|
|
// first lit byte. We use the scissors so that the
|
|
// first 'xBias' bits of the byte will not be displayed.
|
|
//
|
|
// (What we're doing is simply aligning the monochrome
|
|
// blt using the hardware clipping.)
|
|
|
|
MM_SCISSORS_L(ppdev, pjMmBase, xLeft);
|
|
xLeft -= xBias;
|
|
cx += xBias;
|
|
}
|
|
|
|
MM_CUR_X(ppdev, pjMmBase, xLeft);
|
|
MM_CUR_Y(ppdev, pjMmBase, yTop);
|
|
MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, cx - 1);
|
|
MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cy - 1);
|
|
|
|
lDelta = (cxGlyph + 7) >> 3;
|
|
pjGlyph += (yTop - ptlOrigin.y) * lDelta
|
|
+ ((xLeft - ptlOrigin.x) >> 3);
|
|
cj = (cx + 7) >> 3;
|
|
|
|
IO_GP_WAIT(ppdev);
|
|
|
|
MM_CMD(ppdev, pjMmBase,
|
|
(RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
|
|
DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE | BYTE_SWAP | BUS_SIZE_16));
|
|
|
|
CHECK_DATA_READY(ppdev);
|
|
|
|
// We use byte transfers because I don't expect we'll be
|
|
// asked to clip many large glyphs where it would be
|
|
// worth the overhead of setting up for word transfers:
|
|
|
|
do {
|
|
MM_TRANSFER_BYTE(ppdev, pjMmBase, pjGlyph, cj);
|
|
pjGlyph += lDelta;
|
|
|
|
} while (--cy != 0);
|
|
|
|
CHECK_DATA_COMPLETE(ppdev);
|
|
|
|
if (xBias != 0)
|
|
{
|
|
// Reset the scissors if we used it:
|
|
|
|
IO_FIFO_WAIT(ppdev, 1);
|
|
MM_ABS_SCISSORS_L(ppdev, pjMmBase, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
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******************************\
|
|
* CACHEDFONT* pcfAllocateCachedFont()
|
|
*
|
|
* Initializes our font data structure.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
CACHEDFONT* pcfAllocateCachedFont(
|
|
PDEV* ppdev)
|
|
{
|
|
CACHEDFONT* pcf;
|
|
CACHEDGLYPH** ppcg;
|
|
LONG i;
|
|
|
|
pcf = EngAllocMem(FL_ZERO_MEMORY, sizeof(CACHEDFONT), ALLOC_TAG);
|
|
|
|
if (pcf != NULL)
|
|
{
|
|
// Note that we rely on FL_ZERO_MEMORY to zero 'pgaChain' and
|
|
// 'cjAlloc':
|
|
|
|
pcf->cgSentinel.hg = HGLYPH_SENTINEL;
|
|
|
|
// Initialize the hash table entries to all point to our sentinel:
|
|
|
|
for (ppcg = &pcf->apcg[0], i = GLYPH_HASH_SIZE; i != 0; i--, ppcg++)
|
|
{
|
|
*ppcg = &pcf->cgSentinel;
|
|
}
|
|
}
|
|
|
|
return(pcf);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vFreeCachedFont()
|
|
*
|
|
* Frees all memory associated with the cache we kept for this font.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vFreeCachedFont(
|
|
CACHEDFONT* pcf)
|
|
{
|
|
GLYPHALLOC* pga;
|
|
GLYPHALLOC* pgaNext;
|
|
|
|
|
|
pga = pcf->pgaChain;
|
|
while (pga != NULL)
|
|
{
|
|
pgaNext = pga->pgaNext;
|
|
EngFreeMem(pga);
|
|
pga = pgaNext;
|
|
}
|
|
|
|
EngFreeMem(pcf);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vTrimAndBitpackGlyph
|
|
*
|
|
* This routine takes a GDI byte-aligned glyphbits definition, trims off
|
|
* any unused pixels on the sides, and creates a bit-packed result that
|
|
* is a natural for the S3's monochrome expansion capabilities.
|
|
* "Bit-packed" is where a small monochrome bitmap is packed with no
|
|
* unused bits between strides. So if GDI gives us a 16x16 bitmap to
|
|
* represent '.' that really only has a 2x2 array of lit pixels, we would
|
|
* trim the result to give a single byte value of 0xf0.
|
|
*
|
|
* Use this routine if your monochrome expansion hardware can do bit-packed
|
|
* expansion (this is the fastest method). If your hardware requires byte-,
|
|
* word-, or dword-alignment on monochrome expansions, use
|
|
* vTrimAndPackGlyph().
|
|
*
|
|
* (This driver doesn't use this routine only because the hardware can't do
|
|
* bit-packing!)
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vTrimAndBitpackGlyph(
|
|
BYTE* pjBuf, // Note: Routine may touch preceding byte!
|
|
BYTE* pjGlyph,
|
|
LONG* pcxGlyph,
|
|
LONG* pcyGlyph,
|
|
POINTL* pptlOrigin,
|
|
LONG* pcj) // For returning the count of bytes of the result
|
|
{
|
|
LONG cxGlyph;
|
|
LONG cyGlyph;
|
|
POINTL ptlOrigin;
|
|
LONG cAlign;
|
|
LONG lDelta;
|
|
BYTE* pj;
|
|
BYTE jBit;
|
|
LONG cjSrcWidth;
|
|
LONG lSrcSkip;
|
|
LONG lDstSkip;
|
|
LONG cRem;
|
|
BYTE* pjSrc;
|
|
BYTE* pjDst;
|
|
LONG i;
|
|
LONG j;
|
|
BYTE jSrc;
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// Trim the glyph
|
|
|
|
cyGlyph = *pcyGlyph;
|
|
cxGlyph = *pcxGlyph;
|
|
ptlOrigin = *pptlOrigin;
|
|
cAlign = 0;
|
|
|
|
lDelta = (cxGlyph + 7) >> 3;
|
|
|
|
// Trim off any zero rows at the bottom of the glyph:
|
|
|
|
pj = pjGlyph + cyGlyph * lDelta; // One past last byte in glyph
|
|
while (cyGlyph > 0)
|
|
{
|
|
i = lDelta;
|
|
do {
|
|
if (*(--pj) != 0)
|
|
goto Done_Bottom_Trim;
|
|
} while (--i != 0);
|
|
|
|
// The entire last row has no lit pixels, so simply skip it:
|
|
|
|
cyGlyph--;
|
|
}
|
|
|
|
ASSERTDD(cyGlyph == 0, "cyGlyph should only be zero here");
|
|
|
|
// We found a space character. Set both dimensions to zero, so
|
|
// that it's easy to special-case later:
|
|
|
|
cxGlyph = 0;
|
|
|
|
Done_Bottom_Trim:
|
|
|
|
// If cxGlyph != 0, we know that the glyph has at least one non-zero
|
|
// row and column. By exploiting this knowledge, we can simplify our
|
|
// end-of-loop tests, because we don't have to check to see if we've
|
|
// decremented either 'cyGlyph' or 'cxGlyph' to zero:
|
|
|
|
if (cxGlyph != 0)
|
|
{
|
|
// Trim off any zero rows at the top of the glyph:
|
|
|
|
pj = pjGlyph; // First byte in glyph
|
|
while (TRUE)
|
|
{
|
|
i = lDelta;
|
|
do {
|
|
if (*(pj++) != 0)
|
|
goto Done_Top_Trim;
|
|
} while (--i != 0);
|
|
|
|
// The entire first row has no lit pixels, so simply skip it:
|
|
|
|
cyGlyph--;
|
|
ptlOrigin.y++;
|
|
pjGlyph = pj;
|
|
}
|
|
|
|
Done_Top_Trim:
|
|
|
|
// Trim off any zero columns at the right edge of the glyph:
|
|
|
|
while (TRUE)
|
|
{
|
|
j = cxGlyph - 1;
|
|
|
|
pj = pjGlyph + (j >> 3); // Last byte in first row of glyph
|
|
jBit = gajBit[j & 0x7];
|
|
i = cyGlyph;
|
|
|
|
do {
|
|
if ((*pj & jBit) != 0)
|
|
goto Done_Right_Trim;
|
|
|
|
pj += lDelta;
|
|
} while (--i != 0);
|
|
|
|
// The entire last column has no lit pixels, so simply skip it:
|
|
|
|
cxGlyph--;
|
|
}
|
|
|
|
Done_Right_Trim:
|
|
|
|
// Trim off any zero columns at the left edge of the glyph:
|
|
|
|
while (TRUE)
|
|
{
|
|
pj = pjGlyph; // First byte in first row of glyph
|
|
jBit = gajBit[cAlign];
|
|
i = cyGlyph;
|
|
|
|
do {
|
|
if ((*pj & jBit) != 0)
|
|
goto Done_Left_Trim;
|
|
|
|
pj += lDelta;
|
|
} while (--i != 0);
|
|
|
|
// The entire first column has no lit pixels, so simply skip it:
|
|
|
|
ptlOrigin.x++;
|
|
cxGlyph--;
|
|
cAlign++;
|
|
if (cAlign >= 8)
|
|
{
|
|
cAlign = 0;
|
|
pjGlyph++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Done_Left_Trim:
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// Pack the glyph
|
|
|
|
cjSrcWidth = (cxGlyph + cAlign + 7) >> 3;
|
|
lSrcSkip = lDelta - cjSrcWidth;
|
|
lDstSkip = ((cxGlyph + 7) >> 3) - cjSrcWidth - 1;
|
|
cRem = ((cxGlyph - 1) & 7) + 1; // 0 -> 8
|
|
|
|
pjSrc = pjGlyph;
|
|
pjDst = pjBuf;
|
|
|
|
// Zero the buffer, because we're going to 'or' stuff into it:
|
|
|
|
memset(pjBuf, 0, (cxGlyph * cyGlyph + 7) >> 3);
|
|
|
|
// cAlign used to indicate which bit in the first byte of the unpacked
|
|
// glyph was the first non-zero pixel column. Now, we flip it to
|
|
// indicate which bit in the packed byte will receive the next non-zero
|
|
// glyph bit:
|
|
|
|
cAlign = (-cAlign) & 0x7;
|
|
if (cAlign > 0)
|
|
{
|
|
// It would be bad if our trimming calculations were wrong, because
|
|
// we assume any bits to the left of the 'cAlign' bit will be zero.
|
|
// As a result of this decrement, we will 'or' those zero bits into
|
|
// whatever byte precedes the glyph bits array:
|
|
|
|
pjDst--;
|
|
|
|
ASSERTDD((*pjSrc >> cAlign) == 0, "Trimmed off too many bits");
|
|
}
|
|
|
|
for (i = cyGlyph; i != 0; i--)
|
|
{
|
|
for (j = cjSrcWidth; j != 0; j--)
|
|
{
|
|
// Note that we may modify a byte past the end of our
|
|
// destination buffer, which is why we reserved an
|
|
// extra byte:
|
|
|
|
jSrc = *pjSrc;
|
|
*(pjDst) |= (jSrc >> (cAlign));
|
|
*(pjDst + 1) |= (jSrc << (8 - cAlign));
|
|
pjSrc++;
|
|
pjDst++;
|
|
}
|
|
|
|
pjSrc += lSrcSkip;
|
|
pjDst += lDstSkip;
|
|
cAlign += cRem;
|
|
|
|
if (cAlign >= 8)
|
|
{
|
|
cAlign -= 8;
|
|
pjDst++;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// Return results
|
|
|
|
*pcxGlyph = cxGlyph;
|
|
*pcyGlyph = cyGlyph;
|
|
*pptlOrigin = ptlOrigin;
|
|
*pcj = ((cxGlyph * cyGlyph) + 7) >> 3;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vTrimAndPackGlyph
|
|
*
|
|
* This routine takes a GDI byte-aligned glyphbits definition, trims off
|
|
* any unused pixels on the sides, and creates a word-algined result that
|
|
* is a natural for the S3's monochrome expansion capabilities.
|
|
* So if GDI gives us a 16x16 bitmap to represent '.' that really only
|
|
* has a 2x2 array of lit pixels, we would trim the result to give 2 words
|
|
* of 0xc000 and 0xc000.
|
|
*
|
|
* Use this routine if your monochrome expansion hardware requires byte-,
|
|
* word-, or dword-alignment on monochrome expansions. If your hardware
|
|
* can do bit-packed expansions, please use vTrimAndBitpackGlyph(), since
|
|
* it will be faster.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vTrimAndPackGlyph(
|
|
PDEV* ppdev,
|
|
BYTE* pjBuf, // Note: Routine may touch preceding byte!
|
|
BYTE* pjGlyph,
|
|
LONG* pcxGlyph,
|
|
LONG* pcyGlyph,
|
|
POINTL* pptlOrigin,
|
|
LONG* pcj) // For returning the count of bytes of the result
|
|
{
|
|
LONG cxGlyph;
|
|
LONG cyGlyph;
|
|
POINTL ptlOrigin;
|
|
LONG cAlign;
|
|
LONG lDelta;
|
|
BYTE* pj;
|
|
BYTE jBit;
|
|
LONG cjSrcWidth;
|
|
LONG lSrcSkip;
|
|
LONG lDstSkip;
|
|
LONG lDstDelta;
|
|
BYTE* pjSrc;
|
|
BYTE* pjDst;
|
|
LONG i;
|
|
LONG j;
|
|
BYTE jSrc;
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// Trim the glyph
|
|
|
|
cyGlyph = *pcyGlyph;
|
|
cxGlyph = *pcxGlyph;
|
|
ptlOrigin = *pptlOrigin;
|
|
cAlign = 0;
|
|
|
|
// let [x] denote the least integer greater than or equal to x
|
|
// Set lDelta to be [cxGlyph/8]. This is the number of bytes occupied
|
|
// by the pixels in the horizontal direction of the monochrome glyph.
|
|
|
|
lDelta = (cxGlyph + 7) >> 3;
|
|
|
|
// Trim off any zero rows at the bottom of the glyph:
|
|
|
|
pj = pjGlyph + cyGlyph * lDelta; // One past last byte in glyph
|
|
while (cyGlyph > 0)
|
|
{
|
|
i = lDelta;
|
|
do {
|
|
if (*(--pj) != 0)
|
|
goto Done_Bottom_Trim;
|
|
} while (--i != 0);
|
|
|
|
// The entire last row has no lit pixels, so simply skip it:
|
|
|
|
cyGlyph--;
|
|
}
|
|
|
|
ASSERTDD(cyGlyph == 0, "cyGlyph should only be zero here");
|
|
|
|
// We found a space character. Set both dimensions to zero, so
|
|
// that it's easy to special-case later:
|
|
|
|
cxGlyph = 0;
|
|
|
|
Done_Bottom_Trim:
|
|
|
|
// If cxGlyph != 0, we know that the glyph has at least one non-zero
|
|
// row and column. By exploiting this knowledge, we can simplify our
|
|
// end-of-loop tests, because we don't have to check to see if we've
|
|
// decremented either 'cyGlyph' or 'cxGlyph' to zero:
|
|
|
|
if (cxGlyph != 0)
|
|
{
|
|
// Trim off any zero rows at the top of the glyph:
|
|
|
|
pj = pjGlyph; // First byte in glyph
|
|
while (TRUE)
|
|
{
|
|
i = lDelta;
|
|
do {
|
|
if (*(pj++) != 0)
|
|
goto Done_Top_Trim;
|
|
} while (--i != 0);
|
|
|
|
// The entire first row has no lit pixels, so simply skip it:
|
|
|
|
cyGlyph--;
|
|
ptlOrigin.y++;
|
|
pjGlyph = pj;
|
|
}
|
|
|
|
Done_Top_Trim:
|
|
|
|
// Trim off any zero columns at the right edge of the glyph:
|
|
|
|
while (TRUE)
|
|
{
|
|
j = cxGlyph - 1;
|
|
|
|
pj = pjGlyph + (j >> 3); // Last byte in first row of glyph
|
|
jBit = gajBit[j & 0x7];
|
|
i = cyGlyph;
|
|
|
|
do {
|
|
if ((*pj & jBit) != 0)
|
|
goto Done_Right_Trim;
|
|
|
|
pj += lDelta;
|
|
} while (--i != 0);
|
|
|
|
// The entire last column has no lit pixels, so simply skip it:
|
|
|
|
cxGlyph--;
|
|
}
|
|
|
|
Done_Right_Trim:
|
|
|
|
// Trim off any zero columns at the left edge of the glyph:
|
|
|
|
while (TRUE)
|
|
{
|
|
pj = pjGlyph; // First byte in first row of glyph
|
|
jBit = gajBit[cAlign];
|
|
i = cyGlyph;
|
|
|
|
do {
|
|
if ((*pj & jBit) != 0)
|
|
goto Done_Left_Trim;
|
|
|
|
pj += lDelta;
|
|
} while (--i != 0);
|
|
|
|
// The entire first column has no lit pixels, so simply skip it:
|
|
|
|
ptlOrigin.x++;
|
|
cxGlyph--;
|
|
cAlign++;
|
|
if (cAlign >= 8)
|
|
{
|
|
cAlign = 0;
|
|
pjGlyph++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Done_Left_Trim:
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// Pack the glyph
|
|
|
|
// byte count of cell size (trimmed width + blank left columns).
|
|
|
|
cjSrcWidth = (cxGlyph + cAlign + 7) >> 3;
|
|
|
|
// difference between cell width and trimmed glyph width.
|
|
|
|
lSrcSkip = lDelta - cjSrcWidth;
|
|
|
|
// trimmed glyph width in bytes.
|
|
|
|
lDstDelta = (cxGlyph + 7) >> 3;
|
|
|
|
// Make the glyphs 'word-packed' (i.e., every scan is word aligned)
|
|
// unless in 24bpp mode, in which case we have to use 32 bit bus size,
|
|
// which in turn requires dword packing.
|
|
|
|
if (ppdev->iBitmapFormat == BMF_24BPP)
|
|
lDstDelta = (lDstDelta + 3) & ~3;
|
|
else
|
|
lDstDelta = (lDstDelta + 1) & ~1;
|
|
|
|
lDstSkip = lDstDelta - cjSrcWidth;
|
|
|
|
pjSrc = pjGlyph; // Start of trimmed glyph, not including empty left columns.
|
|
pjDst = pjBuf;
|
|
|
|
// Zero the first byte of the buffer, because we're going to 'or' stuff
|
|
// into it:
|
|
|
|
*pjDst = 0;
|
|
|
|
// cAlign used to indicate which bit in the first byte of the unpacked
|
|
// glyph was the first non-zero pixel column. Now, we flip it to
|
|
// indicate which bit in the packed byte will receive the next non-zero
|
|
// glyph bit:
|
|
|
|
cAlign = (-cAlign) & 0x7;
|
|
if (cAlign > 0)
|
|
{
|
|
// It would be bad if our trimming calculations were wrong, because
|
|
// we assume any bits to the left of the 'cAlign' bit will be zero.
|
|
// As a result of this decrement, we will 'or' those zero bits into
|
|
// whatever byte precedes the glyph bits array:
|
|
|
|
pjDst--;
|
|
|
|
ASSERTDD((*pjSrc >> cAlign) == 0, "Trimmed off too many bits");
|
|
}
|
|
|
|
for (i = cyGlyph; i != 0; i--)
|
|
{
|
|
for (j = cjSrcWidth; j != 0; j--)
|
|
{
|
|
// Note that we may modify a byte past the end of our
|
|
// destination buffer, which is why we reserved an
|
|
// extra byte:
|
|
jSrc = *pjSrc;
|
|
*(pjDst) |= (jSrc >> (cAlign));
|
|
*(pjDst + 1) = (jSrc << (8 - cAlign));
|
|
pjSrc++;
|
|
pjDst++;
|
|
|
|
}
|
|
pjSrc += lSrcSkip;
|
|
pjDst += lDstSkip;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// Return results
|
|
|
|
*pcxGlyph = cxGlyph;
|
|
*pcyGlyph = cyGlyph;
|
|
*pptlOrigin = ptlOrigin;
|
|
*pcj = lDstDelta * cyGlyph;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* LONG cjPutGlyphInCache
|
|
*
|
|
* Figures out where to be a glyph in off-screen memory, copies it
|
|
* there, and fills in any other data we'll need to display the glyph.
|
|
*
|
|
* This routine is rather device-specific, and will have to be extensively
|
|
* modified for other display adapters.
|
|
*
|
|
* Returns the number of bytes taken by the cached glyph bits.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
LONG cjPutGlyphInCache(
|
|
PDEV* ppdev,
|
|
CACHEDGLYPH* pcg,
|
|
GLYPHBITS* pgb)
|
|
{
|
|
BYTE* pjGlyph;
|
|
LONG cxGlyph;
|
|
LONG cyGlyph;
|
|
POINTL ptlOrigin;
|
|
BYTE* pjSrc;
|
|
ULONG* pulDst;
|
|
LONG i;
|
|
LONG cPels;
|
|
ULONG ulGlyphThis;
|
|
ULONG ulGlyphNext;
|
|
ULONG ul;
|
|
ULONG ulStart;
|
|
LONG cj;
|
|
|
|
pjGlyph = pgb->aj;
|
|
cyGlyph = pgb->sizlBitmap.cy;
|
|
cxGlyph = pgb->sizlBitmap.cx;
|
|
ptlOrigin = pgb->ptlOrigin;
|
|
|
|
vTrimAndPackGlyph(ppdev, (BYTE*) &pcg->ad, pjGlyph, &cxGlyph, &cyGlyph,
|
|
&ptlOrigin, &cj);
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// Initialize the glyph fields
|
|
|
|
pcg->ptlOrigin = ptlOrigin;
|
|
pcg->cxLessOne = cxGlyph - 1;
|
|
pcg->cyLessOne = cyGlyph - 1;
|
|
pcg->cxcyLessOne = PACKXY(cxGlyph - 1, cyGlyph - 1);
|
|
pcg->cw = (cj + 1) >> 1;
|
|
pcg->cd = (cj + 3) >> 2;
|
|
|
|
return(cj);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CACHEDGLYPH* pcgNew()
|
|
*
|
|
* Creates a new CACHEDGLYPH structure for keeping track of the glyph in
|
|
* off-screen memory. bPutGlyphInCache is called to actually put the glyph
|
|
* in off-screen memory.
|
|
*
|
|
* This routine should be reasonably device-independent, as bPutGlyphInCache
|
|
* will contain most of the code that will have to be modified for other
|
|
* display adapters.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
CACHEDGLYPH* pcgNew(
|
|
PDEV* ppdev,
|
|
CACHEDFONT* pcf,
|
|
GLYPHPOS* pgp)
|
|
{
|
|
GLYPHBITS* pgb;
|
|
GLYPHALLOC* pga;
|
|
CACHEDGLYPH* pcg;
|
|
LONG cjCachedGlyph;
|
|
HGLYPH hg;
|
|
LONG iHash;
|
|
CACHEDGLYPH* pcgFind;
|
|
LONG cjGlyphRow;
|
|
LONG cj;
|
|
|
|
// First, calculate the amount of storage we'll need for this glyph:
|
|
|
|
pgb = pgp->pgdf->pgb;
|
|
|
|
// The glyphs are 'word-packed':
|
|
|
|
cjGlyphRow = ((pgb->sizlBitmap.cx + 15) & ~15) >> 3;
|
|
cjCachedGlyph = sizeof(CACHEDGLYPH) + (pgb->sizlBitmap.cy * cjGlyphRow);
|
|
|
|
// Reserve an extra byte at the end for temporary usage by our pack
|
|
// routine:
|
|
|
|
cjCachedGlyph++;
|
|
|
|
if (cjCachedGlyph > pcf->cjAlloc)
|
|
{
|
|
// Have to allocate a new glyph allocation structure:
|
|
|
|
pga = EngAllocMem(FL_ZERO_MEMORY, GLYPH_ALLOC_SIZE, ALLOC_TAG);
|
|
if (pga == NULL)
|
|
{
|
|
// It's safe to return at this time because we haven't
|
|
// fatally altered any of our data structures:
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
// Add this allocation to the front of the allocation linked list,
|
|
// so that we can free it later:
|
|
|
|
pga->pgaNext = pcf->pgaChain;
|
|
pcf->pgaChain = pga;
|
|
|
|
// Now we've got a chunk of memory where we can store our cached
|
|
// glyphs:
|
|
|
|
pcf->pcgNew = &pga->acg[0];
|
|
pcf->cjAlloc = GLYPH_ALLOC_SIZE - (sizeof(*pga) - sizeof(pga->acg[0]));
|
|
|
|
// It would be bad if we let in any glyphs that would be bigger
|
|
// than our basic allocation size:
|
|
|
|
ASSERTDD(cjCachedGlyph <= GLYPH_ALLOC_SIZE, "Woah, this is one big glyph!");
|
|
}
|
|
|
|
pcg = pcf->pcgNew;
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// Insert the glyph, in-order, into the list hanging off our hash
|
|
// bucket:
|
|
|
|
hg = pgp->hg;
|
|
|
|
pcg->hg = hg;
|
|
iHash = GLYPH_HASH_FUNC(hg);
|
|
pcgFind = pcf->apcg[iHash];
|
|
|
|
if (pcgFind->hg > hg)
|
|
{
|
|
pcf->apcg[iHash] = pcg;
|
|
pcg->pcgNext = pcgFind;
|
|
}
|
|
else
|
|
{
|
|
// The sentinel will ensure that we never fall off the end of
|
|
// this list:
|
|
|
|
while (pcgFind->pcgNext->hg < hg)
|
|
pcgFind = pcgFind->pcgNext;
|
|
|
|
// 'pcgFind' now points to the entry to the entry after which
|
|
// we want to insert our new node:
|
|
|
|
pcg->pcgNext = pcgFind->pcgNext;
|
|
pcgFind->pcgNext = pcg;
|
|
}
|
|
|
|
cj = cjPutGlyphInCache(ppdev, pcg, pgp->pgdf->pgb);
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// We now know the size taken up by the packed and trimmed glyph;
|
|
// adjust the pointer to the next glyph accordingly. We only need
|
|
// to ensure 'dword' alignment:
|
|
|
|
cjCachedGlyph = sizeof(CACHEDGLYPH) + ((cj + 7) & ~7);
|
|
|
|
pcf->pcgNew = (CACHEDGLYPH*) ((BYTE*) pcg + cjCachedGlyph);
|
|
pcf->cjAlloc -= cjCachedGlyph;
|
|
|
|
return(pcg);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bMmCachedProportionalText
|
|
*
|
|
* Draws proportionally spaced glyphs via glyph caching.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bMmCachedProportionalText(
|
|
PDEV* ppdev,
|
|
CACHEDFONT* pcf,
|
|
GLYPHPOS* pgp,
|
|
LONG cGlyph)
|
|
{
|
|
BYTE* pjMmBase;
|
|
LONG xOffset;
|
|
LONG yOffset;
|
|
HGLYPH hg;
|
|
CACHEDGLYPH* pcg;
|
|
LONG cyLessOne;
|
|
LONG x;
|
|
LONG y;
|
|
|
|
pjMmBase = ppdev->pjMmBase;
|
|
xOffset = ppdev->xOffset;
|
|
yOffset = ppdev->yOffset;
|
|
|
|
// Ensure that there is enough room in the FIFO for the
|
|
// coordinate and dimensions of the first glyph, so that we
|
|
// don't accidentally hold the bus for a long to while a
|
|
// previous big operation, such as a screen-to-screen blt,
|
|
// is done.
|
|
|
|
IO_FIFO_WAIT(ppdev, 4);
|
|
|
|
do {
|
|
hg = pgp->hg;
|
|
pcg = pcf->apcg[GLYPH_HASH_FUNC(hg)];
|
|
|
|
while (pcg->hg < hg)
|
|
pcg = pcg->pcgNext; // Traverse collision list, if any
|
|
|
|
if (pcg->hg > hg)
|
|
{
|
|
// This will hopefully not be the common case (that is,
|
|
// we will have a high cache hit rate), so if I were
|
|
// writing this in Asm I would have this out-of-line
|
|
// to avoid the jump around for the common case.
|
|
// But the Pentium has branch prediction, so what the
|
|
// heck.
|
|
|
|
pcg = pcgNew(ppdev, pcf, pgp);
|
|
if (pcg == NULL)
|
|
return(FALSE);
|
|
}
|
|
|
|
// Space glyphs are trimmed to a height of zero, and we don't
|
|
// even have to touch the hardware for them:
|
|
|
|
cyLessOne = pcg->cyLessOne;
|
|
|
|
if (cyLessOne >= 0)
|
|
{
|
|
x = pgp->ptl.x + pcg->ptlOrigin.x + xOffset;
|
|
y = pgp->ptl.y + pcg->ptlOrigin.y + yOffset;
|
|
|
|
DBG_FAKE_WAIT(ppdev, pjMmBase, 4); // For debug builds only
|
|
|
|
MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, pcg->cxLessOne);
|
|
MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cyLessOne);
|
|
|
|
MM_ABS_CUR_X(ppdev, pjMmBase, x);
|
|
MM_ABS_CUR_Y(ppdev, pjMmBase, y);
|
|
IO_GP_WAIT(ppdev);
|
|
|
|
MM_CMD(ppdev, pjMmBase,
|
|
(RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
|
|
DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE | BYTE_SWAP | BUS_SIZE_16));
|
|
|
|
CHECK_DATA_READY(ppdev);
|
|
|
|
MM_TRANSFER_WORD_ALIGNED(ppdev, pjMmBase, &pcg->ad[0], pcg->cw);
|
|
|
|
CHECK_DATA_COMPLETE(ppdev);
|
|
}
|
|
} while (pgp++, --cGlyph != 0);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bMmCachedClippedText
|
|
*
|
|
* Draws clipped text via glyph caching.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bMmCachedClippedText(
|
|
PDEV* ppdev,
|
|
CACHEDFONT* pcf,
|
|
STROBJ* pstro,
|
|
CLIPOBJ* pco)
|
|
{
|
|
BOOL bRet;
|
|
BYTE* pjMmBase;
|
|
LONG xOffset;
|
|
LONG yOffset;
|
|
BOOL bMoreGlyphs;
|
|
ULONG cGlyphOriginal;
|
|
ULONG cGlyph;
|
|
BOOL bClippingSet;
|
|
GLYPHPOS* pgpOriginal;
|
|
GLYPHPOS* pgp;
|
|
LONG xGlyph;
|
|
LONG yGlyph;
|
|
LONG x;
|
|
LONG y;
|
|
LONG xRight;
|
|
LONG cyLessOne;
|
|
BOOL bMore;
|
|
CLIPENUM ce;
|
|
RECTL* prclClip;
|
|
ULONG ulCharInc;
|
|
HGLYPH hg;
|
|
CACHEDGLYPH* pcg;
|
|
BYTE iDComplexity;
|
|
|
|
bRet = TRUE;
|
|
pjMmBase = ppdev->pjMmBase;
|
|
xOffset = ppdev->xOffset;
|
|
yOffset = ppdev->yOffset;
|
|
ulCharInc = pstro->ulCharInc;
|
|
|
|
// Ensure that there is enough room in the FIFO for the
|
|
// coordinate and dimensions of the first glyph, so that we
|
|
// don't accidentally hold the bus for a long to while a
|
|
// previous big operation, such as a screen-to-screen blt,
|
|
// is done.
|
|
|
|
IO_FIFO_WAIT(ppdev, 4);
|
|
|
|
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);
|
|
}
|
|
|
|
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
|
|
|
|
if (cGlyphOriginal > 0)
|
|
{
|
|
if (iDComplexity != DC_COMPLEX)
|
|
{
|
|
// 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;
|
|
ce.c = 1;
|
|
|
|
if (iDComplexity == DC_TRIVIAL)
|
|
prclClip = &grclMax;
|
|
else
|
|
prclClip = &pco->rclBounds;
|
|
|
|
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:
|
|
|
|
// We don't always simply set the clipping rectangle here
|
|
// because it may actually end up that no text intersects
|
|
// this clip rectangle, so it would be for naught. This
|
|
// actually happens a lot when using NT's analog clock set
|
|
// to always-on-top, with a round shape:
|
|
|
|
bClippingSet = FALSE;
|
|
|
|
pgp = pgpOriginal;
|
|
cGlyph = cGlyphOriginal;
|
|
|
|
// We can't yet convert to absolute coordinates by adding
|
|
// in 'xOffset' or 'yOffset' here because we have yet to
|
|
// compare the coordinates to 'prclClip':
|
|
|
|
xGlyph = pgp->ptl.x;
|
|
yGlyph = pgp->ptl.y;
|
|
|
|
// Loop through all the glyphs for this rectangle:
|
|
|
|
while (TRUE)
|
|
{
|
|
hg = pgp->hg;
|
|
pcg = pcf->apcg[GLYPH_HASH_FUNC(hg)];
|
|
|
|
while (pcg->hg < hg)
|
|
pcg = pcg->pcgNext;
|
|
|
|
if (pcg->hg > hg)
|
|
{
|
|
// This will hopefully not be the common case (that is,
|
|
// we will have a high cache hit rate), so if I were
|
|
// writing this in Asm I would have this out-of-line
|
|
// to avoid the jump around for the common case.
|
|
// But the Pentium has branch prediction, so what the
|
|
// heck.
|
|
|
|
pcg = pcgNew(ppdev, pcf, pgp);
|
|
if (pcg == NULL)
|
|
{
|
|
bRet = FALSE;
|
|
goto AllDone;
|
|
}
|
|
}
|
|
|
|
// Space glyphs are trimmed to a height of zero, and we don't
|
|
// even have to touch the hardware for them:
|
|
|
|
cyLessOne = pcg->cyLessOne;
|
|
if (cyLessOne >= 0)
|
|
{
|
|
y = pcg->ptlOrigin.y + yGlyph;
|
|
x = pcg->ptlOrigin.x + xGlyph;
|
|
xRight = pcg->cxLessOne + x;
|
|
|
|
// Do trivial rejection:
|
|
|
|
if ((prclClip->right > x) &&
|
|
(prclClip->bottom > y) &&
|
|
(prclClip->left <= xRight) &&
|
|
(prclClip->top <= y + cyLessOne))
|
|
{
|
|
// Lazily set the hardware clipping:
|
|
|
|
if ((iDComplexity != DC_TRIVIAL) && (!bClippingSet))
|
|
{
|
|
bClippingSet = TRUE;
|
|
vSetClipping(ppdev, prclClip);
|
|
|
|
// Wait here for same reason we do IO_FIFO_WAIT(4) above...
|
|
|
|
IO_FIFO_WAIT(ppdev, 4);
|
|
}
|
|
|
|
DBG_FAKE_WAIT(ppdev, pjMmBase, 4); // For debug builds only
|
|
|
|
MM_MAJ_AXIS_PCNT(ppdev, pjMmBase, pcg->cxLessOne);
|
|
MM_MIN_AXIS_PCNT(ppdev, pjMmBase, cyLessOne);
|
|
|
|
MM_ABS_CUR_X(ppdev, pjMmBase, xOffset + x);
|
|
MM_ABS_CUR_Y(ppdev, pjMmBase, yOffset + y);
|
|
|
|
IO_GP_WAIT(ppdev);
|
|
|
|
MM_CMD(ppdev, pjMmBase,
|
|
(RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
|
|
DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE | BYTE_SWAP | BUS_SIZE_16));
|
|
|
|
CHECK_DATA_READY(ppdev);
|
|
|
|
MM_TRANSFER_WORD_ALIGNED(ppdev, pjMmBase, &pcg->ad[0], pcg->cw);
|
|
|
|
CHECK_DATA_COMPLETE(ppdev);
|
|
}
|
|
}
|
|
|
|
if (--cGlyph == 0)
|
|
break;
|
|
|
|
// Get ready for next glyph:
|
|
|
|
pgp++;
|
|
|
|
if (ulCharInc == 0)
|
|
{
|
|
xGlyph = pgp->ptl.x;
|
|
yGlyph = pgp->ptl.y;
|
|
}
|
|
else
|
|
{
|
|
xGlyph += ulCharInc;
|
|
}
|
|
}
|
|
}
|
|
} while (bMore);
|
|
}
|
|
} while (bMoreGlyphs);
|
|
|
|
AllDone:
|
|
|
|
if (iDComplexity != DC_TRIVIAL)
|
|
{
|
|
vResetClipping(ppdev);
|
|
}
|
|
|
|
return(bRet);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bMmTextOut
|
|
*
|
|
* Outputs text using the 'buffer expansion' method. The CPU draws to a
|
|
* 1bpp buffer, and the result is colour-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.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bMmTextOut(
|
|
SURFOBJ* pso,
|
|
STROBJ* pstro,
|
|
FONTOBJ* pfo,
|
|
CLIPOBJ* pco,
|
|
RECTL* prclOpaque,
|
|
BRUSHOBJ* pboFore,
|
|
BRUSHOBJ* pboOpaque)
|
|
{
|
|
PDEV* ppdev;
|
|
DSURF* pdsurf;
|
|
BYTE* pjMmBase;
|
|
BOOL bGlyphExpand;
|
|
BOOL bTextPerfectFit;
|
|
ULONG cGlyph;
|
|
BOOL bMoreGlyphs;
|
|
GLYPHPOS* pgp;
|
|
GLYPHBITS* pgb;
|
|
BYTE* pjGlyph;
|
|
LONG cyGlyph;
|
|
POINTL ptlOrigin;
|
|
LONG ulCharInc;
|
|
BYTE iDComplexity;
|
|
LONG lDelta;
|
|
LONG cw;
|
|
RECTL rclOpaque;
|
|
CACHEDFONT* pcf;
|
|
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
|
|
pjMmBase = ppdev->pjMmBase;
|
|
|
|
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
|
|
|
|
if (prclOpaque != NULL)
|
|
{
|
|
////////////////////////////////////////////////////////////
|
|
// Opaque Initialization
|
|
////////////////////////////////////////////////////////////
|
|
|
|
if (iDComplexity == DC_TRIVIAL)
|
|
{
|
|
|
|
DrawOpaqueRect:
|
|
|
|
IO_FIFO_WAIT(ppdev, 8);
|
|
MM_FRGD_COLOR(ppdev, pjMmBase, pboOpaque->iSolidColor);
|
|
MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
|
|
MM_FRGD_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | OVERPAINT);
|
|
MM_CUR_X(ppdev, pjMmBase, prclOpaque->left);
|
|
MM_CUR_Y(ppdev, pjMmBase, prclOpaque->top);
|
|
MM_MAJ_AXIS_PCNT(ppdev, pjMmBase,
|
|
prclOpaque->right - prclOpaque->left - 1);
|
|
MM_MIN_AXIS_PCNT(ppdev, pjMmBase,
|
|
prclOpaque->bottom - prclOpaque->top - 1);
|
|
|
|
MM_CMD(ppdev, pjMmBase, RECTANGLE_FILL | DRAWING_DIR_TBLRXM |
|
|
DRAW | DIR_TYPE_XY |
|
|
LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE);
|
|
}
|
|
else if (iDComplexity == DC_RECT)
|
|
{
|
|
if (bIntersect(prclOpaque, &pco->rclBounds, &rclOpaque))
|
|
{
|
|
prclOpaque = &rclOpaque;
|
|
goto DrawOpaqueRect;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vClipSolid(ppdev, 1, prclOpaque, pboOpaque->iSolidColor, pco);
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
// If the glyphs don't overlap, we can lay the glyphs down
|
|
// in 'opaque' mode, which on the S3 I've found to be faster
|
|
// than opaque mode:
|
|
|
|
IO_FIFO_WAIT(ppdev, 7);
|
|
|
|
MM_PIX_CNTL(ppdev, pjMmBase, CPU_DATA);
|
|
MM_FRGD_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | OVERPAINT);
|
|
MM_BKGD_MIX(ppdev, pjMmBase, BACKGROUND_COLOR | OVERPAINT);
|
|
MM_FRGD_COLOR(ppdev, pjMmBase, pboFore->iSolidColor);
|
|
MM_BKGD_COLOR(ppdev, pjMmBase, pboOpaque->iSolidColor);
|
|
goto SkipTransparentInitialization;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Transparent Initialization
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// Initialize the hardware for transparent text:
|
|
|
|
IO_FIFO_WAIT(ppdev, 4);
|
|
|
|
MM_PIX_CNTL(ppdev, pjMmBase, CPU_DATA);
|
|
MM_FRGD_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | OVERPAINT);
|
|
MM_BKGD_MIX(ppdev, pjMmBase, BACKGROUND_COLOR | LEAVE_ALONE);
|
|
MM_FRGD_COLOR(ppdev, pjMmBase, pboFore->iSolidColor);
|
|
|
|
SkipTransparentInitialization:
|
|
|
|
if ((pfo->cxMax <= GLYPH_CACHE_CX) &&
|
|
((pstro->rclBkGround.bottom - pstro->rclBkGround.top) <= GLYPH_CACHE_CY))
|
|
{
|
|
pcf = (CACHEDFONT*) pfo->pvConsumer;
|
|
|
|
if (pcf == NULL)
|
|
{
|
|
pcf = pcfAllocateCachedFont(ppdev);
|
|
if (pcf == NULL)
|
|
return(FALSE);
|
|
|
|
pfo->pvConsumer = pcf;
|
|
}
|
|
|
|
// Use our glyph cache:
|
|
|
|
if ((iDComplexity == DC_TRIVIAL) && (pstro->ulCharInc == 0))
|
|
{
|
|
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 (!bMmCachedProportionalText(ppdev, pcf, pgp, cGlyph))
|
|
return(FALSE);
|
|
}
|
|
} while (bMoreGlyphs);
|
|
}
|
|
else
|
|
{
|
|
if (!bMmCachedClippedText(ppdev, pcf, pstro, pco))
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DISPDBG((4, "Text too big to cache: %li x %li",
|
|
pfo->cxMax, pstro->rclBkGround.bottom - pstro->rclBkGround.top));
|
|
|
|
vMmGeneralText(ppdev, pstro, pco);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bNwCachedProportionalText
|
|
*
|
|
* Draws proportionally spaced glyphs via glyph caching.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bNwCachedProportionalText(
|
|
PDEV* ppdev,
|
|
CACHEDFONT* pcf,
|
|
GLYPHPOS* pgp,
|
|
LONG cGlyph)
|
|
{
|
|
BYTE* pjMmBase;
|
|
LONG xOffset;
|
|
LONG yOffset;
|
|
HGLYPH hg;
|
|
CACHEDGLYPH* pcg;
|
|
LONG cxcyLessOne;
|
|
LONG x;
|
|
LONG y;
|
|
USHORT busmode = BUS_SIZE_16;
|
|
|
|
pjMmBase = ppdev->pjMmBase;
|
|
xOffset = ppdev->xOffset;
|
|
yOffset = ppdev->yOffset;
|
|
|
|
// Ensure that there is enough room in the FIFO for the
|
|
// coordinate and dimensions of the first glyph, so that we
|
|
// don't accidentally hold the bus for a long to while a
|
|
// previous big operation, such as a screen-to-screen blt,
|
|
// is done.
|
|
|
|
NW_FIFO_WAIT(ppdev, pjMmBase, 2);
|
|
|
|
if (ppdev->iBitmapFormat == BMF_24BPP)
|
|
busmode = BUS_SIZE_32;
|
|
|
|
do {
|
|
hg = pgp->hg;
|
|
pcg = pcf->apcg[GLYPH_HASH_FUNC(hg)];
|
|
|
|
while (pcg->hg < hg)
|
|
pcg = pcg->pcgNext; // Traverse collision list, if any
|
|
|
|
if (pcg->hg > hg)
|
|
{
|
|
// This will hopefully not be the common case (that is,
|
|
// we will have a high cache hit rate), so if I were
|
|
// writing this in Asm I would have this out-of-line
|
|
// to avoid the jump around for the common case.
|
|
// But the Pentium has branch prediction, so what the
|
|
// heck.
|
|
|
|
pcg = pcgNew(ppdev, pcf, pgp);
|
|
if (pcg == NULL)
|
|
return(FALSE);
|
|
}
|
|
|
|
// Space glyphs are trimmed to a height of zero, and we don't
|
|
// even have to touch the hardware for them:
|
|
|
|
cxcyLessOne = pcg->cxcyLessOne;
|
|
|
|
if (cxcyLessOne >= 0)
|
|
{
|
|
x = pgp->ptl.x + pcg->ptlOrigin.x + xOffset;
|
|
y = pgp->ptl.y + pcg->ptlOrigin.y + yOffset;
|
|
|
|
DBG_FAKE_WAIT(ppdev, pjMmBase, 2); // For debug builds only
|
|
|
|
NW_ABS_CURXY_FAST(ppdev, pjMmBase, x, y);
|
|
NW_ALT_PCNT_PACKED(ppdev, pjMmBase, cxcyLessOne);
|
|
|
|
NW_GP_WAIT(ppdev, pjMmBase);
|
|
|
|
NW_ALT_CMD(ppdev, pjMmBase,
|
|
(RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
|
|
DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE | BYTE_SWAP | busmode));
|
|
|
|
CHECK_DATA_READY(ppdev);
|
|
|
|
#if defined(_X86_)
|
|
|
|
memcpy(pjMmBase, &pcg->ad[0], pcg->cd << 2);
|
|
|
|
#else
|
|
|
|
// Non-x86 platforms may be required to call the HAL to
|
|
// the I/O, or to do memory barriers:
|
|
|
|
MM_TRANSFER_DWORD_ALIGNED(ppdev, pjMmBase, &pcg->ad[0], pcg->cd);
|
|
|
|
#endif
|
|
|
|
CHECK_DATA_COMPLETE(ppdev);
|
|
}
|
|
} while (pgp++, --cGlyph != 0);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bNwCachedClippedText
|
|
*
|
|
* Draws clipped text via glyph caching.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bNwCachedClippedText(
|
|
PDEV* ppdev,
|
|
CACHEDFONT* pcf,
|
|
STROBJ* pstro,
|
|
CLIPOBJ* pco)
|
|
{
|
|
BOOL bRet;
|
|
BYTE* pjMmBase;
|
|
LONG xOffset;
|
|
LONG yOffset;
|
|
BOOL bMoreGlyphs;
|
|
ULONG cGlyphOriginal;
|
|
ULONG cGlyph;
|
|
BOOL bClippingSet;
|
|
GLYPHPOS* pgpOriginal;
|
|
GLYPHPOS* pgp;
|
|
LONG xGlyph;
|
|
LONG yGlyph;
|
|
LONG x;
|
|
LONG y;
|
|
LONG xRight;
|
|
LONG cyLessOne;
|
|
BOOL bMore;
|
|
CLIPENUM ce;
|
|
RECTL* prclClip;
|
|
ULONG ulCharInc;
|
|
HGLYPH hg;
|
|
CACHEDGLYPH* pcg;
|
|
BYTE iDComplexity;
|
|
USHORT busmode = BUS_SIZE_16;
|
|
|
|
bRet = TRUE;
|
|
pjMmBase = ppdev->pjMmBase;
|
|
xOffset = ppdev->xOffset;
|
|
yOffset = ppdev->yOffset;
|
|
ulCharInc = pstro->ulCharInc;
|
|
|
|
// Ensure that there is enough room in the FIFO for the
|
|
// coordinate and dimensions of the first glyph, so that we
|
|
// don't accidentally hold the bus for a long to while a
|
|
// previous big operation, such as a screen-to-screen blt,
|
|
// is done.
|
|
|
|
NW_FIFO_WAIT(ppdev, pjMmBase, 2);
|
|
|
|
if (ppdev->iBitmapFormat == BMF_24BPP)
|
|
busmode = BUS_SIZE_32;
|
|
|
|
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);
|
|
}
|
|
|
|
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
|
|
|
|
if (cGlyphOriginal > 0)
|
|
{
|
|
if (iDComplexity != DC_COMPLEX)
|
|
{
|
|
// 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;
|
|
ce.c = 1;
|
|
|
|
if (iDComplexity == DC_TRIVIAL)
|
|
prclClip = &grclMax;
|
|
else
|
|
prclClip = &pco->rclBounds;
|
|
|
|
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:
|
|
|
|
// We don't always simply set the clipping rectangle here
|
|
// because it may actually end up that no text intersects
|
|
// this clip rectangle, so it would be for naught. This
|
|
// actually happens a lot when using NT's analog clock set
|
|
// to always-on-top, with a round shape:
|
|
|
|
bClippingSet = FALSE;
|
|
|
|
pgp = pgpOriginal;
|
|
cGlyph = cGlyphOriginal;
|
|
|
|
// We can't yet convert to absolute coordinates by adding
|
|
// in 'xOffset' or 'yOffset' here because we have yet to
|
|
// compare the coordinates to 'prclClip':
|
|
|
|
xGlyph = pgp->ptl.x;
|
|
yGlyph = pgp->ptl.y;
|
|
|
|
// Loop through all the glyphs for this rectangle:
|
|
|
|
while (TRUE)
|
|
{
|
|
hg = pgp->hg;
|
|
pcg = pcf->apcg[GLYPH_HASH_FUNC(hg)];
|
|
|
|
while (pcg->hg < hg)
|
|
pcg = pcg->pcgNext;
|
|
|
|
if (pcg->hg > hg)
|
|
{
|
|
// This will hopefully not be the common case (that is,
|
|
// we will have a high cache hit rate), so if I were
|
|
// writing this in Asm I would have this out-of-line
|
|
// to avoid the jump around for the common case.
|
|
// But the Pentium has branch prediction, so what the
|
|
// heck.
|
|
|
|
pcg = pcgNew(ppdev, pcf, pgp);
|
|
if (pcg == NULL)
|
|
{
|
|
bRet = FALSE;
|
|
goto AllDone;
|
|
}
|
|
}
|
|
|
|
// Space glyphs are trimmed to a height of zero, and we don't
|
|
// even have to touch the hardware for them:
|
|
|
|
cyLessOne = pcg->cyLessOne;
|
|
if (cyLessOne >= 0)
|
|
{
|
|
y = pcg->ptlOrigin.y + yGlyph;
|
|
x = pcg->ptlOrigin.x + xGlyph;
|
|
xRight = pcg->cxLessOne + x;
|
|
|
|
// Do trivial rejection:
|
|
|
|
if ((prclClip->right > x) &&
|
|
(prclClip->bottom > y) &&
|
|
(prclClip->left <= xRight) &&
|
|
(prclClip->top <= y + cyLessOne))
|
|
{
|
|
// Lazily set the hardware clipping:
|
|
|
|
if ((iDComplexity != DC_TRIVIAL) && (!bClippingSet))
|
|
{
|
|
bClippingSet = TRUE;
|
|
vSetClipping(ppdev, prclClip);
|
|
|
|
// Wait here for same reason we do NW_FIFO_WAIT(2) above...
|
|
|
|
NW_FIFO_WAIT(ppdev, pjMmBase, 2);
|
|
}
|
|
|
|
DBG_FAKE_WAIT(ppdev, pjMmBase, 2); // For debug builds only
|
|
|
|
NW_ABS_CURXY(ppdev, pjMmBase, xOffset + x, yOffset + y);
|
|
NW_ALT_PCNT_PACKED(ppdev, pjMmBase, pcg->cxcyLessOne);
|
|
|
|
NW_GP_WAIT(ppdev, pjMmBase);
|
|
|
|
NW_ALT_CMD(ppdev, pjMmBase,
|
|
(RECTANGLE_FILL | WAIT | DRAWING_DIR_TBLRXM |
|
|
DRAW | LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE | BYTE_SWAP | busmode));
|
|
|
|
CHECK_DATA_READY(ppdev);
|
|
|
|
MM_TRANSFER_DWORD_ALIGNED(ppdev, pjMmBase, &pcg->ad[0], pcg->cd);
|
|
|
|
CHECK_DATA_COMPLETE(ppdev);
|
|
}
|
|
}
|
|
|
|
if (--cGlyph == 0)
|
|
break;
|
|
|
|
// Get ready for next glyph:
|
|
|
|
pgp++;
|
|
|
|
if (ulCharInc == 0)
|
|
{
|
|
xGlyph = pgp->ptl.x;
|
|
yGlyph = pgp->ptl.y;
|
|
}
|
|
else
|
|
{
|
|
xGlyph += ulCharInc;
|
|
}
|
|
}
|
|
}
|
|
} while (bMore);
|
|
}
|
|
} while (bMoreGlyphs);
|
|
|
|
AllDone:
|
|
|
|
if (iDComplexity != DC_TRIVIAL)
|
|
{
|
|
vResetClipping(ppdev);
|
|
}
|
|
|
|
return(bRet);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bNwTextOut
|
|
*
|
|
* Outputs text using the 'buffer expansion' method. The CPU draws to a
|
|
* 1bpp buffer, and the result is colour-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.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bNwTextOut(
|
|
SURFOBJ* pso,
|
|
STROBJ* pstro,
|
|
FONTOBJ* pfo,
|
|
CLIPOBJ* pco,
|
|
RECTL* prclOpaque,
|
|
BRUSHOBJ* pboFore,
|
|
BRUSHOBJ* pboOpaque)
|
|
{
|
|
PDEV* ppdev;
|
|
DSURF* pdsurf;
|
|
BYTE* pjMmBase;
|
|
BOOL bGlyphExpand;
|
|
BOOL bTextPerfectFit;
|
|
ULONG cGlyph;
|
|
BOOL bMoreGlyphs;
|
|
GLYPHPOS* pgp;
|
|
GLYPHBITS* pgb;
|
|
BYTE* pjGlyph;
|
|
LONG cyGlyph;
|
|
POINTL ptlOrigin;
|
|
LONG ulCharInc;
|
|
BYTE iDComplexity;
|
|
LONG lDelta;
|
|
LONG cw;
|
|
RECTL rclOpaque;
|
|
CACHEDFONT* pcf;
|
|
LONG xOffset;
|
|
LONG yOffset;
|
|
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
|
|
pjMmBase = ppdev->pjMmBase;
|
|
xOffset = ppdev->xOffset;
|
|
yOffset = ppdev->yOffset;
|
|
|
|
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
|
|
|
|
if (prclOpaque != NULL)
|
|
{
|
|
////////////////////////////////////////////////////////////
|
|
// Opaque Initialization
|
|
////////////////////////////////////////////////////////////
|
|
|
|
if (iDComplexity == DC_TRIVIAL)
|
|
{
|
|
|
|
DrawOpaqueRect:
|
|
|
|
NW_FIFO_WAIT(ppdev, pjMmBase, 6);
|
|
NW_FRGD_COLOR(ppdev, pjMmBase, pboOpaque->iSolidColor);
|
|
MM_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
|
|
MM_FRGD_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | OVERPAINT);
|
|
NW_ABS_CURXY_FAST(ppdev, pjMmBase, prclOpaque->left + xOffset,
|
|
prclOpaque->top + yOffset);
|
|
NW_ALT_PCNT(ppdev, pjMmBase,
|
|
prclOpaque->right - prclOpaque->left - 1,
|
|
prclOpaque->bottom - prclOpaque->top - 1);
|
|
|
|
NW_ALT_CMD(ppdev, pjMmBase, RECTANGLE_FILL | DRAWING_DIR_TBLRXM |
|
|
DRAW | DIR_TYPE_XY |
|
|
LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
|
WRITE);
|
|
}
|
|
else if (iDComplexity == DC_RECT)
|
|
{
|
|
if (bIntersect(prclOpaque, &pco->rclBounds, &rclOpaque))
|
|
{
|
|
prclOpaque = &rclOpaque;
|
|
goto DrawOpaqueRect;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vClipSolid(ppdev, 1, prclOpaque, pboOpaque->iSolidColor, pco);
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
// If the glyphs don't overlap, we can lay the glyphs down
|
|
// in 'opaque' mode, which on the S3 I've found to be faster
|
|
// than opaque mode:
|
|
|
|
NW_FIFO_WAIT(ppdev, pjMmBase, 4);
|
|
|
|
MM_PIX_CNTL(ppdev, pjMmBase, CPU_DATA);
|
|
NW_ALT_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | OVERPAINT,
|
|
BACKGROUND_COLOR | OVERPAINT);
|
|
NW_FRGD_COLOR(ppdev, pjMmBase, pboFore->iSolidColor);
|
|
NW_BKGD_COLOR(ppdev, pjMmBase, pboOpaque->iSolidColor);
|
|
goto SkipTransparentInitialization;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Transparent Initialization
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// Initialize the hardware for transparent text:
|
|
|
|
NW_FIFO_WAIT(ppdev, pjMmBase, 3);
|
|
|
|
MM_PIX_CNTL(ppdev, pjMmBase, CPU_DATA);
|
|
NW_ALT_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | OVERPAINT,
|
|
BACKGROUND_COLOR | LEAVE_ALONE);
|
|
NW_FRGD_COLOR(ppdev, pjMmBase, pboFore->iSolidColor);
|
|
|
|
SkipTransparentInitialization:
|
|
|
|
if ((pfo->cxMax <= GLYPH_CACHE_CX) &&
|
|
((pstro->rclBkGround.bottom - pstro->rclBkGround.top) <= GLYPH_CACHE_CY))
|
|
{
|
|
pcf = (CACHEDFONT*) pfo->pvConsumer;
|
|
|
|
if (pcf == NULL)
|
|
{
|
|
pcf = pcfAllocateCachedFont(ppdev);
|
|
if (pcf == NULL)
|
|
return(FALSE);
|
|
|
|
pfo->pvConsumer = pcf;
|
|
}
|
|
|
|
// Use our glyph cache:
|
|
|
|
if ((iDComplexity == DC_TRIVIAL) && (pstro->ulCharInc == 0))
|
|
{
|
|
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 (!bNwCachedProportionalText(ppdev, pcf, pgp, cGlyph))
|
|
return(FALSE);
|
|
}
|
|
} while (bMoreGlyphs);
|
|
}
|
|
else
|
|
{
|
|
if (!bNwCachedClippedText(ppdev, pcf, pstro, pco))
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DISPDBG((4, "Text too big to cache: %li x %li",
|
|
pfo->cxMax, pstro->rclBkGround.bottom - pstro->rclBkGround.top));
|
|
|
|
// Can't do large glyphs via accelerator at 24bpp:
|
|
|
|
if (ppdev->iBitmapFormat == BMF_24BPP)
|
|
{
|
|
BANK bnk;
|
|
BOOL b = TRUE;
|
|
|
|
vBankStart(ppdev,
|
|
(prclOpaque!= NULL) ? prclOpaque : &pstro->rclBkGround,
|
|
pco,
|
|
&bnk);
|
|
do {
|
|
b &= EngTextOut(bnk.pso,
|
|
pstro,
|
|
pfo,
|
|
bnk.pco,
|
|
NULL,
|
|
prclOpaque,
|
|
pboFore,
|
|
pboOpaque,
|
|
NULL,
|
|
0x0d0d);
|
|
|
|
} while (bBankEnum(&bnk));
|
|
|
|
return b;
|
|
}
|
|
|
|
vMmGeneralText(ppdev, pstro, pco);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL DrvTextOut
|
|
*
|
|
* Calls the appropriate text drawing routine.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
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, // Always unused, unless GCAPS_ARBRUSHOPAQUE set
|
|
MIX mix) // Always a copy mix -- 0x0d0d
|
|
{
|
|
PDEV* ppdev;
|
|
DSURF* pdsurf;
|
|
|
|
pdsurf = (DSURF*) pso->dhsurf;
|
|
ppdev = (PDEV*) pso->dhpdev;
|
|
|
|
ASSERTDD(!(pdsurf->dt & DT_DIB), "Didn't expect DT_DIB");
|
|
|
|
ppdev->xOffset = pdsurf->x;
|
|
ppdev->yOffset = pdsurf->y;
|
|
|
|
// There seems to be a problem with 24 bpp accelerated large text
|
|
// on s3 diamond 968 so for now, punt to GDI
|
|
|
|
// 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");
|
|
|
|
return(ppdev->pfnTextOut(pso, pstro, pfo, pco, prclOpaque, pboFore,
|
|
pboOpaque));
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bEnableText
|
|
*
|
|
* Performs the necessary setup for the text drawing subcomponent.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bEnableText(
|
|
PDEV* ppdev)
|
|
{
|
|
SIZEL sizl;
|
|
HBITMAP hbm;
|
|
|
|
if (ppdev->pfnTextOut == bIoTextOut)
|
|
{
|
|
// We need to allocate a temporary 1bpp surface object if we're
|
|
// going to have GDI draw the glyphs for us:
|
|
|
|
sizl.cx = ppdev->cxMemory;
|
|
sizl.cy = ppdev->cyMemory;
|
|
|
|
// We will be mucking with the surface's 'pvScan0' value, so we
|
|
// simply must pass in a non-NULL 'pvBits' value to EngCreateBitmap:
|
|
|
|
hbm = EngCreateBitmap(sizl, sizl.cx, BMF_1BPP, 0, ppdev->pvTmpBuffer);
|
|
if (hbm == 0)
|
|
return(FALSE);
|
|
|
|
ppdev->psoText = EngLockSurface((HSURF) hbm);
|
|
if (ppdev->psoText == NULL)
|
|
{
|
|
EngDeleteSurface((HSURF) hbm);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vDisableText
|
|
*
|
|
* Performs the necessary clean-up for the text drawing subcomponent.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vDisableText(PDEV* ppdev)
|
|
{
|
|
HSURF hsurf;
|
|
SURFOBJ* psoText;
|
|
|
|
// Here we free any stuff allocated in 'bEnableText'.
|
|
|
|
psoText = ppdev->psoText;
|
|
|
|
if (psoText != NULL)
|
|
{
|
|
hsurf = psoText->hsurf;
|
|
|
|
EngUnlockSurface(psoText);
|
|
EngDeleteSurface(hsurf);
|
|
}
|
|
}
|
|
|
|
/******************************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
|
|
*
|
|
* Note: Don't forget to export this call in 'enable.c', otherwise you'll
|
|
* get some pretty big memory leaks!
|
|
*
|
|
* 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)
|
|
{
|
|
CACHEDFONT* pcf;
|
|
|
|
pcf = pfo->pvConsumer;
|
|
if (pcf != NULL)
|
|
{
|
|
vFreeCachedFont(pcf);
|
|
pfo->pvConsumer = NULL;
|
|
}
|
|
}
|