|
|
/******************************Module*Header*******************************\
* Module Name: textout.c * * We cache off-screen glyphs linearly in off-screen memory. * * Copyright (c) 1992-1996 Microsoft Corporation * Copyright (c) 1993-1996 Matrox Electronic Systems, Ltd. \**************************************************************************/
#include "precomp.h"
//////////////////////////////////////////////////////////////////////////
BYTE gajBit[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; // Converts bit index to set bit
RECTL grclMax = { 0, 0, 0x10000, 0x10000 }; // Maximal clip rectangle for trivial clipping
// Array used for getting the mirror image of bytes:
BYTE gajFlip[] = { 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, };
/******************************Public*Routine******************************\
* VOID vClipSolid * * Fills the specified rectangle with the specified colour, honouring * the requested clipping. * \**************************************************************************/
VOID vClipSolid( PDEV* ppdev, RECTL* prcl, ULONG iColor, CLIPOBJ* pco) { BOOL bMore; // Flag for clip enumeration
CLIPENUM ce; // Clip enumeration object
LONG c; // Count of non-empty rectangles
RBRUSH_COLOR rbc; // For passing colour to vFillSolid
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
// Scan through all the clip rectangles, looking for intersects
// of fill areas with region rectangles:
rbc.iSolidColor = iColor;
do { // Get a batch of region rectangles:
bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (VOID*) &ce);
c = cIntersect(prcl, ce.arcl, ce.c);
if (c != 0) ppdev->pfnFillSolid(ppdev, c, ce.arcl, 0xf0f0, rbc, NULL);
} while (bMore); }
/******************************Public*Routine******************************\
* VOID vExpandGlyph * \**************************************************************************/
VOID vExpandGlyph( PDEV* ppdev, BYTE* pj, // Can be unaligned
LONG lSkip, LONG cj, LONG cy, BOOL bHwBug) { BYTE* pjBase; ULONG* pulDma; LONG cFifo; LONG cd; ULONG UNALIGNED* pulSrc; LONG cEdgeCases; LONG i; ULONG ul;
ASSERTDD((bHwBug == 1) || (bHwBug == 0), "Expect binary bHwBug");
pjBase = ppdev->pjBase; pulDma = (ULONG*) (pjBase + DMAWND); cd = cj >> 2; pulSrc = (ULONG UNALIGNED*) pj;
CHECK_FIFO_SPACE(pjBase, FIFOSIZE); BLT_WRITE_ON(ppdev, pjBase);
// Make sure we have room for very first write that accounts for
// the hardware bug:
cFifo = FIFOSIZE - 1; CHECK_FIFO_SPACE(pjBase, FIFOSIZE);
// If we have to work around the hardware bug, we usually have to have
// two FIFO entries open for the work-around write, and for the last
// edge write:
cEdgeCases = 1 + bHwBug;
switch (cj & 3) { case 0: cEdgeCases = bHwBug; // No last edge write
do { if (bHwBug) CP_WRITE_DMA(ppdev, pulDma, 0); // Account for hardware bug
for (i = cd; i != 0; i--) { if (--cFifo < 0) { cFifo = FIFOSIZE - 1; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); } CP_WRITE_DMA(ppdev, pulDma, *pulSrc++); }
if ((cFifo -= cEdgeCases) < 0) { cFifo = FIFOSIZE - 1; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); } pulSrc = (ULONG UNALIGNED*) ((BYTE*) pulSrc + lSkip);
} while (--cy != 0); break;
case 1: do { if (bHwBug) CP_WRITE_DMA(ppdev, pulDma, 0); // Account for hardware bug
for (i = cd; i != 0; i--) { if (--cFifo < 0) { cFifo = FIFOSIZE - 1; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); } CP_WRITE_DMA(ppdev, pulDma, *pulSrc++); }
if ((cFifo -= cEdgeCases) < 0) { cFifo = FIFOSIZE - 2; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); } CP_WRITE_DMA(ppdev, pulDma, *((BYTE*) pulSrc)); pulSrc = (ULONG UNALIGNED*) ((BYTE*) pulSrc + lSkip + 1);
} while (--cy != 0); break;
case 2: do { if (bHwBug) CP_WRITE_DMA(ppdev, pulDma, 0); // Account for hardware bug
for (i = cd; i != 0; i--) { if (--cFifo < 0) { cFifo = FIFOSIZE - 1; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); } CP_WRITE_DMA(ppdev, pulDma, *pulSrc++); }
if ((cFifo -= cEdgeCases) < 0) { cFifo = FIFOSIZE - 2; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); } CP_WRITE_DMA(ppdev, pulDma, *((WORD UNALIGNED *) pulSrc)); pulSrc = (ULONG UNALIGNED*) ((BYTE*) pulSrc + lSkip + 2);
} while (--cy != 0); break;
case 3: do { if (bHwBug) CP_WRITE_DMA(ppdev, pulDma, 0); // Account for hardware bug
for (i = cd; i != 0; i--) { if (--cFifo < 0) { cFifo = FIFOSIZE - 1; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); } CP_WRITE_DMA(ppdev, pulDma, *pulSrc++); }
if ((cFifo -= cEdgeCases) < 0) { cFifo = FIFOSIZE - 2; CHECK_FIFO_SPACE(pjBase, FIFOSIZE); } ul = *((WORD UNALIGNED *) pulSrc) | (*(((BYTE*) pulSrc) + 2) << 16); CP_WRITE_DMA(ppdev, pulDma, ul); pulSrc = (ULONG UNALIGNED*) ((BYTE*) pulSrc + lSkip + 3);
} while (--cy != 0); break; }
BLT_WRITE_OFF(ppdev, pjBase); }
/******************************Public*Routine******************************\
* VOID vMgaGeneralText * \**************************************************************************/
VOID vMgaGeneralText( PDEV* ppdev, STROBJ* pstro, CLIPOBJ* pco) { BYTE* pjBase; LONG xOffset; LONG yOffset; BYTE iDComplexity; BOOL bMoreGlyphs; ULONG cGlyphOriginal; ULONG cGlyph; GLYPHPOS* pgpOriginal; GLYPHPOS* pgp; GLYPHBITS* pgb; POINTL ptlOrigin; BOOL bMore; CLIPENUM ce; RECTL* prclClip; ULONG ulCharInc; LONG cxGlyph; LONG cyGlyph; LONG cx; LONG cy; LONG xLeft; LONG yTop; LONG xRight; LONG yBottom; LONG lDelta; LONG cj; BYTE* pjGlyph; BOOL bHwBug; LONG xAlign;
pjBase = ppdev->pjBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
do { if (pstro->pgp != NULL) { // There's only the one batch of glyphs, so save ourselves
// a call:
pgpOriginal = pstro->pgp; cGlyphOriginal = pstro->cGlyphs; bMoreGlyphs = FALSE; } else { bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyphOriginal, &pgpOriginal); }
if (cGlyphOriginal > 0) { ulCharInc = pstro->ulCharInc;
if (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
CHECK_FIFO_SPACE(pjBase, 7); CP_WRITE(pjBase, DWG_LEN, cyGlyph); CP_WRITE(pjBase, DWG_YDST, yOffset + ptlOrigin.y); CP_WRITE(pjBase, DWG_FXLEFT, xOffset + ptlOrigin.x); CP_WRITE(pjBase, DWG_FXRIGHT, xOffset + ptlOrigin.x + cxGlyph - 1);
bHwBug = (cxGlyph >= 128);
if (!bHwBug) { CP_WRITE(pjBase, DWG_SHIFT, 0); CP_WRITE(pjBase, DWG_AR3, 0); CP_START(pjBase, DWG_AR0, cxGlyph - 1); } else { CP_WRITE(pjBase, DWG_AR3, 8); CP_WRITE(pjBase, DWG_AR0, cxGlyph + 31); CP_START(pjBase, DWG_SHIFT, (24 << 16)); }
vExpandGlyph(ppdev, pjGlyph, 0, (cxGlyph + 7) >> 3, cyGlyph, bHwBug); } 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)) { CHECK_FIFO_SPACE(pjBase, 7); CP_WRITE(pjBase, DWG_LEN, cy); CP_WRITE(pjBase, DWG_YDST, yOffset + yTop); CP_WRITE(pjBase, DWG_FXLEFT, xOffset + xLeft); CP_WRITE(pjBase, DWG_FXRIGHT, xOffset + xRight - 1);
xAlign = (xLeft - ptlOrigin.x) & 31;
bHwBug = (cx >= 128) && (xAlign <= 15);
if (!bHwBug) { CP_WRITE(pjBase, DWG_SHIFT, 0); CP_WRITE(pjBase, DWG_AR3, xAlign); CP_START(pjBase, DWG_AR0, xAlign + cx - 1); } else { CP_WRITE(pjBase, DWG_AR3, xAlign + 8); CP_WRITE(pjBase, DWG_AR0, xAlign + cx + 31); CP_START(pjBase, DWG_SHIFT, (24 << 16)); }
lDelta = (cxGlyph + 7) >> 3; pjGlyph += (yTop - ptlOrigin.y) * lDelta + (((xLeft - ptlOrigin.x) >> 3) & ~3); cj = (xAlign + cx + 7) >> 3;
vExpandGlyph(ppdev, pjGlyph, lDelta - cj, cj, cy, bHwBug); } }
if (--cGlyph == 0) break;
// Get ready for next glyph:
pgp++; pgb = pgp->pgdf->pgb;
if (ulCharInc == 0) { ptlOrigin.x = pgp->ptl.x + pgb->ptlOrigin.x; ptlOrigin.y = pgp->ptl.y + pgb->ptlOrigin.y; } else { ptlOrigin.x += ulCharInc; } } } } while (bMore); } } while (bMoreGlyphs); }
/******************************Public*Routine******************************\
* VOID vMilGeneralText * \**************************************************************************/
VOID vMilGeneralText( PDEV* ppdev, STROBJ* pstro, CLIPOBJ* pco) { BYTE* pjBase; LONG xOffset; LONG yOffset; BYTE iDComplexity; BOOL bMoreGlyphs; ULONG cGlyphOriginal; ULONG cGlyph; GLYPHPOS* pgpOriginal; GLYPHPOS* pgp; GLYPHBITS* pgb; POINTL ptlOrigin; BOOL bMore; CLIPENUM ce; RECTL* prclClip; ULONG ulCharInc; LONG cxGlyph; LONG cyGlyph; LONG cx; LONG cy; LONG xLeft; LONG yTop; LONG xRight; LONG yBottom; LONG lDelta; LONG cj; BYTE* pjGlyph; BOOL bHwBug; LONG xAlign; BOOL bClipSet;
pjBase = ppdev->pjBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity; bClipSet = FALSE;
do { if (pstro->pgp != NULL) { // There's only the one batch of glyphs, so save ourselves
// a call:
pgpOriginal = pstro->pgp; cGlyphOriginal = pstro->cGlyphs; bMoreGlyphs = FALSE; } else { bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyphOriginal, &pgpOriginal); }
if (cGlyphOriginal > 0) { ulCharInc = pstro->ulCharInc;
if (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
if (bClipSet) { // A clipped glyph was just drawn.
CHECK_FIFO_SPACE(pjBase, 2); CP_WRITE(pjBase, DWG_CXLEFT, 0); CP_WRITE(pjBase, DWG_CXRIGHT, (ppdev->cxMemory - 1)); bClipSet = FALSE; }
CHECK_FIFO_SPACE(pjBase, 4);
CP_WRITE(pjBase, DWG_FXBNDRY, ((xOffset + ptlOrigin.x + cxGlyph - 1) << bfxright_SHIFT) | ((xOffset + ptlOrigin.x) & bfxleft_MASK));
// ylength_MASK not is needed since coordinates are within range
CP_WRITE(pjBase, DWG_YDSTLEN, ((yOffset + ptlOrigin.y) << yval_SHIFT) | (cyGlyph)); CP_WRITE(pjBase, DWG_AR3, 0); CP_START(pjBase, DWG_AR0, (cxGlyph - 1));
vExpandGlyph(ppdev, pjGlyph, 0, (cxGlyph + 7) >> 3, cyGlyph, FALSE); } 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)) { // We have to set the clipping rectangle.
CHECK_FIFO_SPACE(pjBase, 6); CP_WRITE(pjBase, DWG_CXLEFT, (xOffset + xLeft)); CP_WRITE(pjBase, DWG_CXRIGHT, (xOffset + xRight - 1)); bClipSet = TRUE;
xAlign = (xLeft - ptlOrigin.x) & 0x7; xLeft -= xAlign; cx += xAlign;
CP_WRITE(pjBase, DWG_FXBNDRY, ((xOffset + xRight - 1) << bfxright_SHIFT) | ((xOffset + xLeft) & bfxleft_MASK));
// ylength_MASK not is needed since coordinates are within range
CP_WRITE(pjBase, DWG_YDSTLEN, ((yOffset + yTop) << yval_SHIFT) | (cy));
CP_WRITE(pjBase, DWG_AR3, 0); CP_START(pjBase, DWG_AR0, (cx - 1));
// Send the bits to the DMA window.
lDelta = (cxGlyph + 7) >> 3; pjGlyph += (yTop - ptlOrigin.y) * lDelta + ((xLeft - ptlOrigin.x) >> 3); cj = (cx + 7) >> 3;
vExpandGlyph(ppdev, pjGlyph, lDelta - cj, cj, cy, FALSE); } }
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);
if (bClipSet) { // Clear the clipping registers.
CHECK_FIFO_SPACE(pjBase, 2); CP_WRITE(pjBase, DWG_CXLEFT, 0); CP_WRITE(pjBase, DWG_CXRIGHT, (ppdev->cxMemory - 1)); } }
/******************************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) { // Insert this node into the doubly-linked cached-font list hanging
// off the PDEV:
pcf->pcfNext = ppdev->cfSentinel.pcfNext; pcf->pcfPrev = &ppdev->cfSentinel; ppdev->cfSentinel.pcfNext = pcf; pcf->pcfNext->pcfPrev = pcf;
// 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;
// Remove this node from our cached-font linked-list:
pcf->pcfPrev->pcfNext = pcf->pcfNext; pcf->pcfNext->pcfPrev = pcf->pcfPrev;
// Free all glyph position allocations associated with this font:
pga = pcf->pgaChain; while (pga != NULL) { pgaNext = pga->pgaNext; EngFreeMem(pga); pga = pgaNext; }
EngFreeMem(pcf); }
/******************************Public*Routine******************************\
* VOID vBlowGlyphCache() * \**************************************************************************/
VOID vBlowGlyphCache( PDEV* ppdev) { CACHEDFONT* pcfSentinel; CACHEDFONT* pcf; GLYPHALLOC* pga; GLYPHALLOC* pgaNext; CACHEDGLYPH** ppcg; LONG i;
ASSERTDD(ppdev->flStatus & STAT_GLYPH_CACHE, "No glyph cache to be blown");
// Reset our current glyph variables:
ppdev->ulGlyphCurrent = ppdev->ulGlyphStart;
///////////////////////////////////////////////////////////////////
// Now invalidate all active cached fonts:
pcfSentinel = &ppdev->cfSentinel; for (pcf = pcfSentinel->pcfNext; pcf != pcfSentinel; pcf = pcf->pcfNext) { // Reset all the hash table entries to point to the cached-font
// sentinel. This effectively resets the cache for this font:
for (ppcg = &pcf->apcg[0], i = GLYPH_HASH_SIZE; i != 0; i--, ppcg++) { *ppcg = &pcf->cgSentinel; }
// We may as well free all glyph position allocations for this font:
pga = pcf->pgaChain; while (pga != NULL) { pgaNext = pga->pgaNext; EngFreeMem(pga); pga = pgaNext; }
pcf->pgaChain = NULL; pcf->cjAlloc = 0; } }
/******************************Public*Routine******************************\
* VOID vTrimAndPackGlyph * \**************************************************************************/
VOID vTrimAndPackGlyph( BYTE* pjBuf, // Note: Routine may touch preceding byte!
BYTE* pjGlyph, LONG* pcxGlyph, LONG* pcyGlyph, POINTL* pptlOrigin) { 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; }
/******************************Public*Routine******************************\
* VOID vPackGlyph * \**************************************************************************/
VOID vPackGlyph( BYTE* pjBuf, BYTE* pjGlyph, LONG cxGlyph, LONG cyGlyph) { LONG cjSrcWidth; BYTE jSrc; BYTE* pjSrc; BYTE* pjDst; LONG cAlign; LONG i; LONG j; LONG cRem;
///////////////////////////////////////////////////////////////
// Pack the glyph:
cjSrcWidth = (cxGlyph + 7) >> 3; cRem = ((cxGlyph - 1) & 7) + 1; // 0 -> 8
cAlign = 0;
pjSrc = pjGlyph; pjDst = pjBuf; *pjDst = 0; // Have to zero very first byte
i = cyGlyph; do { j = cjSrcWidth; do { jSrc = *pjSrc;
*(pjDst) |= (jSrc >> (cAlign));
// Note that we may modify a byte past the end of our
// destination buffer, which is why we reserved an
// extra byte:
*(pjDst + 1) = (jSrc << (8 - cAlign));
pjSrc++; pjDst++;
} while (--j != 0);
pjDst--; cAlign += cRem; if (cAlign >= 8) { cAlign -= 8; pjDst++; }
} while (--i != 0); }
/******************************Public*Routine******************************\
* BOOL bPutGlyphInCache * * 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 TRUE if successful; FALSE if there wasn't enough room in * off-screen memory. * \**************************************************************************/
BOOL bPutGlyphInCache( PDEV* ppdev, CACHEDGLYPH* pcg, GLYPHBITS* pgb) { BYTE* pjBase; BYTE* pjGlyph; LONG cxGlyph; LONG cyGlyph; POINTL ptlOrigin; BYTE* pjSrc; ULONG* pulSrc; ULONG* pulDst; LONG i; LONG cPels; ULONG ulGlyphThis; ULONG ulGlyphNext; ULONG ul; ULONG ulStart; BYTE ajBuf[MAX_GLYPH_SIZE + 4]; // Leave room at end for scratch space
pjBase = ppdev->pjBase;
pjGlyph = pgb->aj; cyGlyph = pgb->sizlBitmap.cy; cxGlyph = pgb->sizlBitmap.cx; ptlOrigin = pgb->ptlOrigin;
vTrimAndPackGlyph(&ajBuf[0], pjGlyph, &cxGlyph, &cyGlyph, &ptlOrigin);
ASSERTDD(((cyGlyph * cxGlyph + 7) / 8 + 1) <= sizeof(ajBuf), "Overran end of temporary glyph storage");
///////////////////////////////////////////////////////////////
// Find spot for glyph in off-screen memory
cPels = cyGlyph * cxGlyph; // Note that this may be zero
ulGlyphThis = ppdev->ulGlyphCurrent; ulGlyphNext = ulGlyphThis + ((cPels + 31) & ~31); // Dword aligned
if (ulGlyphNext >= ppdev->ulGlyphEnd) { // There's isn't enough free room in the off-screen cache for another
// glyph. Let the caller know that it should call 'vBlowGlyphCache'
// to free up space.
//
// First, make sure that this glyph will fit in the cache when it's
// empty, too:
ASSERTDD(ppdev->ulGlyphStart + cPels < ppdev->ulGlyphEnd, "Glyph can't fit in empty cache -- where's the higher-level check?");
return(FALSE); }
// Remember where the next glyph goes:
ppdev->ulGlyphCurrent = ulGlyphNext;
///////////////////////////////////////////////////////////////
// Initialize the glyph fields
// Note that cxLessOne and ulLinearEnd will be invalid for a
// 'space' character, so the rendering routine had better watch
// for a height of zero:
pcg->ptlOrigin = ptlOrigin; pcg->cy = cyGlyph; pcg->cxLessOne = cxGlyph - 1; pcg->ulLinearStart = ulGlyphThis; pcg->ulLinearEnd = ulGlyphThis + cPels - 1;
///////////////////////////////////////////////////////////////
// Download the glyph
ulStart = ulGlyphThis >> 3;
// Copy the bit flipped glyph to off-screen:
if (ppdev->ulBoardId == MGA_STORM) { pulSrc = (ULONG*) ajBuf; pulDst = (ULONG*) (ppdev->pjScreen + ulStart);
CHECK_FIFO_SPACE(pjBase, FIFOSIZE);
START_DIRECT_ACCESS_STORM(ppdev, pjBase);
for (i = (cPels + 31) >> 5; i != 0; i--) { *pulDst++ = *pulSrc++; }
END_DIRECT_ACCESS_STORM(ppdev, pjBase); } else { pjSrc = ajBuf; pulDst = (ULONG*) (ppdev->pjBase + SRCWND + (ulStart & 31));
if (ppdev->iBitmapFormat != BMF_8BPP) { // We have to set the plane write mask even when using direct
// access. It doesn't matter at 8bpp:
CHECK_FIFO_SPACE(pjBase, 1); CP_WRITE(pjBase, DWG_PLNWT, plnwt_ALL); }
CHECK_FIFO_SPACE(pjBase, FIFOSIZE);
WAIT_NOT_BUSY(pjBase);
CP_WRITE(pjBase, HST_DSTPAGE, ulStart);
START_DIRECT_ACCESS_MGA_NO_WAIT(ppdev, pjBase);
for (i = (cPels + 31) >> 5; i != 0; i--) { ul = gajFlip[*pjSrc++]; ul |= gajFlip[*pjSrc++] << 8; ul |= gajFlip[*pjSrc++] << 16; ul |= gajFlip[*pjSrc++] << 24;
// The '0' specifies a zero offset from pointer 'pulDst':
CP_WRITE_DIRECT(pulDst, 0, ul);
pulDst++; }
END_DIRECT_ACCESS_MGA(ppdev, pjBase);
if (ppdev->iBitmapFormat != BMF_8BPP) { // Restore the plane write mask:
CHECK_FIFO_SPACE(pjBase, 1); CP_WRITE(pjBase, DWG_PLNWT, ppdev->ulPlnWt); } }
return(TRUE); }
/******************************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) { GLYPHALLOC* pga; CACHEDGLYPH* pcg; LONG cjCachedGlyph; HGLYPH hg; LONG iHash; CACHEDGLYPH* pcgFind;
Restart:
// First, calculate the amount of storage we'll need for this glyph:
cjCachedGlyph = sizeof(CACHEDGLYPH);
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])); }
pcg = pcf->pcgNew;
// We only need to ensure 'dword' alignment of the next structure:
pcf->pcgNew = (CACHEDGLYPH*) ((BYTE*) pcg + cjCachedGlyph); pcf->cjAlloc -= cjCachedGlyph;
///////////////////////////////////////////////////////////////
// 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; }
///////////////////////////////////////////////////////////////
// Download the glyph into off-screen memory:
if (!bPutGlyphInCache(ppdev, pcg, pgp->pgdf->pgb)) { // If there was no more room in off-screen memory, blow the
// glyph cache and start over. Note that this assumes that
// the glyph will fit in the cache when the cache is completely
// empty.
vBlowGlyphCache(ppdev); goto Restart; }
return(pcg); }
/******************************Public*Routine******************************\
* BOOL bMgaCachedProportionalText * * Draws proportionally spaced glyphs via glyph caching for the MGA. * \**************************************************************************/
BOOL bMgaCachedProportionalText( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph) { BYTE* pjBase; LONG xOffset; LONG yOffset; CHAR cFifo; HGLYPH hg; CACHEDGLYPH* pcg; LONG cy; LONG x; LONG y;
pjBase = ppdev->pjBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; cFifo = 0;
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);
cFifo = 0; // Have to reset count
}
// Space glyphs are trimmed to a height of zero, and we don't
// even have to touch the hardware for them:
cy = pcg->cy; if (cy != 0) { x = pgp->ptl.x + pcg->ptlOrigin.x + xOffset; y = pgp->ptl.y + pcg->ptlOrigin.y + yOffset;
// We get a little tricky here and try to amortize the cost of
// the read for checking the FIFO count on the MGA. Doing so
// gave us a 6% and 14% win on 21pt and 16pt text, respectively,
// on a P90:
cFifo -= 6; if (cFifo < 0) { do { cFifo = GET_FIFO_SPACE(pjBase) - 6; } while (cFifo < 0); }
CP_WRITE(pjBase, DWG_LEN, cy); CP_WRITE(pjBase, DWG_YDST, y); CP_WRITE(pjBase, DWG_FXLEFT, x); CP_WRITE(pjBase, DWG_FXRIGHT, x + pcg->cxLessOne); CP_WRITE(pjBase, DWG_AR3, pcg->ulLinearStart); CP_START(pjBase, DWG_AR0, pcg->ulLinearEnd); } } while (pgp++, --cGlyph != 0);
return(TRUE); }
/******************************Public*Routine******************************\
* BOOL bMilCachedProportionalText * * Draws proportionally spaced glyphs via glyph caching for the Millennium. * \**************************************************************************/
BOOL bMilCachedProportionalText( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph) { BYTE* pjBase; LONG xOffset; LONG yOffset; CHAR cFifo; HGLYPH hg; CACHEDGLYPH* pcg; LONG cy; LONG x; LONG y;
pjBase = ppdev->pjBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; cFifo = 0;
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);
cFifo = 0; // Have to reset count
}
// Space glyphs are trimmed to a height of zero, and we don't
// even have to touch the hardware for them:
cy = pcg->cy; if (cy != 0) { x = pgp->ptl.x + pcg->ptlOrigin.x + xOffset; y = pgp->ptl.y + pcg->ptlOrigin.y + yOffset;
cFifo -= 4; if (cFifo < 0) { do { cFifo = GET_FIFO_SPACE(pjBase) - 4; } while (cFifo < 0); }
CP_WRITE(pjBase, DWG_YDSTLEN, (y << yval_SHIFT) | cy); CP_WRITE(pjBase, DWG_FXBNDRY, ((x + pcg->cxLessOne) << bfxright_SHIFT) | x); CP_WRITE(pjBase, DWG_AR3, pcg->ulLinearStart); CP_START(pjBase, DWG_AR0, pcg->ulLinearEnd); } } while (pgp++, --cGlyph != 0);
return(TRUE); }
/******************************Public*Routine******************************\
* BOOL bCachedFixedText * * Draws fixed spaced glyphs via glyph caching. * \**************************************************************************/
BOOL bCachedFixedText( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph, ULONG ulCharInc) { BYTE* pjBase; LONG xGlyph; LONG yGlyph; CHAR cFifo; HGLYPH hg; CACHEDGLYPH* pcg; LONG cy; LONG x; LONG y;
pjBase = ppdev->pjBase; xGlyph = ppdev->xOffset + pgp->ptl.x; yGlyph = ppdev->yOffset + pgp->ptl.y; cFifo = 0;
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);
cFifo = 0; // Have to reset count
}
// Space glyphs are trimmed to a height of zero, and we don't
// even have to touch the hardware for them:
cy = pcg->cy; if (cy != 0) { x = xGlyph + pcg->ptlOrigin.x; y = yGlyph + pcg->ptlOrigin.y;
cFifo -= 6; if (cFifo < 0) { do { cFifo = GET_FIFO_SPACE(pjBase) - 6; } while (cFifo < 0); }
CP_WRITE(pjBase, DWG_LEN, cy); CP_WRITE(pjBase, DWG_YDST, y); CP_WRITE(pjBase, DWG_FXLEFT, x); CP_WRITE(pjBase, DWG_FXRIGHT, x + pcg->cxLessOne); CP_WRITE(pjBase, DWG_AR3, pcg->ulLinearStart); CP_START(pjBase, DWG_AR0, pcg->ulLinearEnd); }
xGlyph += ulCharInc;
} while (pgp++, --cGlyph != 0);
return(TRUE); }
/******************************Public*Routine******************************\
* BOOL bCachedClippedText * * Draws clipped text via glyph caching. * \**************************************************************************/
BOOL bCachedClippedText( PDEV* ppdev, CACHEDFONT* pcf, STROBJ* pstro, CLIPOBJ* pco) { BOOL bRet; BYTE* pjBase; LONG xOffset; LONG yOffset; CHAR cFifo; BOOL bMoreGlyphs; ULONG cGlyphOriginal; ULONG cGlyph; BOOL bClipSet; GLYPHPOS* pgpOriginal; GLYPHPOS* pgp; LONG xGlyph; LONG yGlyph; LONG x; LONG y; LONG xRight; LONG cy; BOOL bMore; CLIPENUM ce; RECTL* prclClip; ULONG ulCharInc; HGLYPH hg; CACHEDGLYPH* pcg;
ASSERTDD((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL), "Don't expect trivial clipping in this function");
bRet = TRUE; pjBase = ppdev->pjBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; ulCharInc = pstro->ulCharInc;
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) { if (pco->iDComplexity == DC_RECT) { // We could call 'cEnumStart' and 'bEnum' when the clipping is
// DC_RECT, but the last time I checked, those two calls took
// more than 150 instructions to go through GDI. Since
// 'rclBounds' already contains the DC_RECT clip rectangle,
// and since it's such a common case, we'll special case it:
bMore = FALSE; ce.c = 1; 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:
bClipSet = 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; } cFifo = 0; // Have to reset count
}
// Space glyphs are trimmed to a height of zero, and we don't
// even have to touch the hardware for them:
cy = pcg->cy; if (cy != 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 + cy)) { // Lazily set the hardware clipping:
if (!bClipSet) { bClipSet = TRUE; vSetClipping(ppdev, prclClip); cFifo = 0; // Have to initialize count
}
cFifo -= 6; if (cFifo < 0) { do { cFifo = GET_FIFO_SPACE(pjBase) - 6; } while (cFifo < 0); }
CP_WRITE(pjBase, DWG_LEN, cy); CP_WRITE(pjBase, DWG_YDST, yOffset + y); CP_WRITE(pjBase, DWG_FXLEFT, xOffset + x); CP_WRITE(pjBase, DWG_FXRIGHT, xOffset + xRight); CP_WRITE(pjBase, DWG_AR3, pcg->ulLinearStart); CP_START(pjBase, DWG_AR0, pcg->ulLinearEnd); } }
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:
vResetClipping(ppdev);
return(bRet); }
/******************************Public*Routine******************************\
* BOOL DrvTextOut * \**************************************************************************/
BOOL DrvTextOut( SURFOBJ* pso, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclExtra, // If we had set GCAPS_HORIZSTRIKE, we would have
// to fill these extra rectangles (it is used
// largely for underlines). It's not a big
// performance win (GDI will call our DrvBitBlt
// to draw the extra rectangles).
RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque, POINTL* pptlBrush, // Always unused, unless GCAPS_ARBRUSHOPAQUE set
MIX mix) // Always a copy mix (0x0d0d)
{ PDEV* ppdev; LONG xOffset; LONG yOffset; DSURF* pdsurf; OH* poh; BYTE* pjBase; ULONG cGlyph; BOOL bMoreGlyphs; GLYPHPOS* pgp; BYTE iDComplexity; CACHEDFONT* pcf; RECTL rclOpaque;
pdsurf = (DSURF*) pso->dhsurf; if (pdsurf->dt != DT_DIB) { poh = pdsurf->poh; ppdev = (PDEV*) pso->dhpdev; xOffset = poh->x; yOffset = poh->y; ppdev->xOffset = xOffset; ppdev->yOffset = yOffset;
// 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");
pjBase = ppdev->pjBase;
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
if (prclOpaque != NULL) { ////////////////////////////////////////////////////////////
// Opaque Initialization
////////////////////////////////////////////////////////////
if (iDComplexity == DC_TRIVIAL) {
DrawOpaqueRect:
if (ppdev->ulBoardId == MGA_STORM) { CHECK_FIFO_SPACE(pjBase, 4); if (ppdev->iBitmapFormat == BMF_24BPP) { CP_WRITE(pjBase, DWG_DWGCTL, opcode_TRAP + atype_RPL + blockm_OFF + pattern_OFF + transc_BG_OPAQUE + arzero_ZERO + sgnzero_ZERO + shftzero_ZERO + solid_SOLID + bop_SRCCOPY); } else { CP_WRITE(pjBase, DWG_DWGCTL, opcode_TRAP + atype_RPL + blockm_ON + pattern_OFF + transc_BG_OPAQUE + arzero_ZERO + sgnzero_ZERO + shftzero_ZERO + solid_SOLID + bop_SRCCOPY); } CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, pboOpaque->iSolidColor)); ppdev->HopeFlags = (SIGN_CACHE | ARX_CACHE | PATTERN_CACHE);
CP_WRITE(pjBase, DWG_FXBNDRY, (((prclOpaque->right + xOffset) << bfxright_SHIFT) | ((prclOpaque->left + xOffset) & bfxleft_MASK)));
// ylength_MASK not is needed since coordinates are within range
CP_START(pjBase, DWG_YDSTLEN, (((prclOpaque->top + yOffset ) << yval_SHIFT) | ((prclOpaque->bottom - prclOpaque->top)))); } else { CHECK_FIFO_SPACE(pjBase, 15);
CP_WRITE(pjBase, DWG_DWGCTL, opcode_TRAP + atype_RPL + blockm_ON + pattern_OFF + transc_BG_OPAQUE + bop_SRCCOPY);
CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, pboOpaque->iSolidColor));
if (!(GET_CACHE_FLAGS(ppdev, SIGN_CACHE))) { CP_WRITE(pjBase, DWG_SGN, 0); }
if (!(GET_CACHE_FLAGS(ppdev, ARX_CACHE))) { CP_WRITE(pjBase, DWG_AR1, 0); CP_WRITE(pjBase, DWG_AR2, 0); CP_WRITE(pjBase, DWG_AR4, 0); CP_WRITE(pjBase, DWG_AR5, 0); }
if (!(GET_CACHE_FLAGS(ppdev, PATTERN_CACHE))) { CP_WRITE(pjBase, DWG_SRC0, 0xFFFFFFFF); CP_WRITE(pjBase, DWG_SRC1, 0xFFFFFFFF); CP_WRITE(pjBase, DWG_SRC2, 0xFFFFFFFF); CP_WRITE(pjBase, DWG_SRC3, 0xFFFFFFFF); }
ppdev->HopeFlags = (SIGN_CACHE | ARX_CACHE | PATTERN_CACHE);
CP_WRITE(pjBase, DWG_FXLEFT, prclOpaque->left + xOffset); CP_WRITE(pjBase, DWG_FXRIGHT, prclOpaque->right + xOffset); CP_WRITE(pjBase, DWG_LEN, prclOpaque->bottom - prclOpaque->top); CP_START(pjBase, DWG_YDST, prclOpaque->top + yOffset); } } else if (iDComplexity == DC_RECT) { if (bIntersect(prclOpaque, &pco->rclBounds, &rclOpaque)) { prclOpaque = &rclOpaque; goto DrawOpaqueRect; } } else { vClipSolid(ppdev, prclOpaque, pboOpaque->iSolidColor, pco); } }
////////////////////////////////////////////////////////////
// Transparent Initialization
////////////////////////////////////////////////////////////
// Initialize the hardware for transparent text:
CHECK_FIFO_SPACE(pjBase, 4);
CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, pboFore->iSolidColor));
if (!(GET_CACHE_FLAGS(ppdev, SIGN_CACHE))) { CP_WRITE(pjBase, DWG_SGN, 0); }
if ((pfo->cxMax <= GLYPH_CACHE_CX) && ((pstro->rclBkGround.bottom - pstro->rclBkGround.top) <= GLYPH_CACHE_CY) && (ppdev->flStatus & STAT_GLYPH_CACHE)) { // Complete setup for transparent monochrome expansions from
// off-screen memory, using block mode if possible:
CP_WRITE(pjBase, DWG_DWGCTL, ppdev->ulTextControl); CP_WRITE(pjBase, DWG_SHIFT, 0);
ppdev->HopeFlags = SIGN_CACHE;
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) { do { if (pstro->pgp != NULL) { // There's only the one batch of glyphs, so save ourselves
// a call:
pgp = pstro->pgp; cGlyph = pstro->cGlyphs; bMoreGlyphs = FALSE; } else { bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyph, &pgp); }
if (cGlyph > 0) { if (pstro->ulCharInc == 0) { if (ppdev->ulBoardId == MGA_STORM) { if (!bMilCachedProportionalText(ppdev, pcf, pgp, cGlyph)) return(FALSE); } else { if (!bMgaCachedProportionalText(ppdev, pcf, pgp, cGlyph)) return(FALSE); } } else { if (!bCachedFixedText(ppdev, pcf, pgp, cGlyph, pstro->ulCharInc)) return(FALSE); } } } while (bMoreGlyphs); } else { if (!bCachedClippedText(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));
// Complete setup for transparent monochrome expansions from the CPU:
CP_WRITE(pjBase, DWG_DWGCTL, (opcode_ILOAD + atype_RPL + blockm_OFF + bop_SRCCOPY + trans_0 + bltmod_BMONO + pattern_OFF + hbgr_SRC_WINDOWS + transc_BG_TRANSP));
if (!(GET_CACHE_FLAGS(ppdev, ARX_CACHE))) { CP_WRITE(pjBase, DWG_AR5, 0); }
ppdev->HopeFlags = SIGN_CACHE;
if (ppdev->ulBoardId == MGA_STORM) { vMilGeneralText(ppdev, pstro, pco); } else { vMgaGeneralText(ppdev, pstro, pco); } } } else { // We're drawing to a DFB we've converted to a DIB, so just call GDI
// to handle it:
return(EngTextOut(pdsurf->pso, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlBrush, mix)); }
return(TRUE); }
/******************************Public*Routine******************************\
* VOID DrvDestroyFont * * 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; } }
/******************************Public*Routine******************************\
* BOOL bEnableText * * Performs the necessary setup for the text drawing subcomponent. * \**************************************************************************/
BOOL bEnableText( PDEV* ppdev) { OH* poh; CACHEDFONT* pcfSentinel; LONG cShift; LONG cFactor;
if (ppdev->ulBoardId == MGA_STORM) { if (ppdev->iBitmapFormat == BMF_24BPP) { ppdev->ulTextControl = opcode_BITBLT + atype_RPL + blockm_OFF + bop_SRCCOPY + trans_0 + bltmod_BMONOWF + pattern_OFF + hbgr_SRC_EG3 + transc_BG_TRANSP + linear_LINEAR_BITBLT; } else { ppdev->ulTextControl = opcode_BITBLT + atype_RPL + blockm_ON + bop_SRCCOPY + trans_0 + bltmod_BMONOWF + pattern_OFF + hbgr_SRC_EG3 + transc_BG_TRANSP + linear_LINEAR_BITBLT; } } else { ppdev->ulTextControl = opcode_BITBLT + atype_RPL + blockm_ON + bop_SRCCOPY + trans_0 + bltmod_BMONO + pattern_OFF + hbgr_SRC_EG3 + transc_BG_TRANSP + linear_LINEAR_BITBLT; }
poh = pohAllocate(ppdev, NULL, ppdev->cxMemory, GLYPH_CACHE_HEIGHT / ppdev->cjPelSize, FLOH_MAKE_PERMANENT); if (poh != NULL) { ppdev->flStatus |= STAT_GLYPH_CACHE;
// Initialize our doubly-linked cached font list:
pcfSentinel = &ppdev->cfSentinel; pcfSentinel->pcfNext = pcfSentinel; pcfSentinel->pcfPrev = pcfSentinel;
// Setup the display adapter specific glyph data.
//
// The linear addresses are computed as bit addresses:
cFactor = ppdev->cjHwPel * 8;
ppdev->ulGlyphStart = (ppdev->ulYDstOrg + poh->y * ppdev->cxMemory) * cFactor;
ppdev->ulGlyphCurrent = ppdev->ulGlyphStart;
ppdev->ulGlyphEnd = (ppdev->ulYDstOrg + (poh->y + poh->cy) * ppdev->cxMemory) * cFactor; }
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) { // Our off-screen glyph cache will get destroyed when we switch to
// full-screen:
if (!bEnable) { if (ppdev->flStatus & STAT_GLYPH_CACHE) { vBlowGlyphCache(ppdev); } } }
|