Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

592 lines
23 KiB

/******************************Module*Header**********************************\
*
* *******************
* * GDI SAMPLE CODE *
* *******************
*
* Module Name: textout.c
*
* Content:
*
* glyph rendering module. Uses glyph caching for P3 and
* glyph expansion for older Glint series accelerators.
*
* Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-2003 Microsoft Corporation. All rights reserved.
\*****************************************************************************/
//@@BEGIN_DDKSPLIT
//
// 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.
//
// 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.
//
// Glyph expansion is typically faster than buffer expansion for very
// large glyphs, even on the ISA bus, because less copying by the CPU
// needs to be done. Unfortunately, large glyphs are pretty rare.
//
// An advantange of the buffer expansion method is that opaque text will
// never flash -- the other two methods typically need to draw the
// opaquing rectangle before laying down the glyphs, which may cause
// a flash if the raster is caught at the wrong time.
//
//@@END_DDKSPLIT
#include "precomp.h"
#include "pxrx.h"
//*********************************************************************************************
// FUNC: vPxRxClipSolid
// ARGS: ppdev (I) - pointer to physical device object
// crcl (I) - number of rectangles
// prcl (I) - array of rectangles
// iColor (I) - the solid fill color
// pco (I) - pointer to the clip region object
// RETN: void
//---------------------------------------------------------------------------------------------
// Fill a series of rectangles clipped by pco with a solid color. This function should only
// be called when the clipping operation is non-trivial
//*********************************************************************************************
VOID vPxRxClipSolid(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;
GLINT_DECL;
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->pgfnFillSolid(ppdev, crcl, prcl, __GLINT_LOGICOP_COPY,
__GLINT_LOGICOP_COPY, rbc, NULL);
}
}
else // iDComplexity == DC_COMPLEX
{
// Bottom of last rectangle to fill
iLastBottom = prcl[crcl - 1].bottom;
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
do {
bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (VOID*)&ce);
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)
{
return; // Past last fill rectangle; nothing left to do
}
if (prclClip->bottom > prcl->top)
{
// We've reached the top Y scan of the first rect, so
// it's worth bothering checking for intersection.
prclTmp = prcl;
prclClipTmp = arclTmp;
for (i = crcl, crclTmp = 0; i--; 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)
{
ppdev->pgfnFillSolid(ppdev, crclTmp, &arclTmp[0],
__GLINT_LOGICOP_COPY, __GLINT_LOGICOP_COPY, rbc, NULL);
}
}
}
} while(bMore);
}
}
//*********************************************************************************************
// FUNC: bPxRxUncachedText
// ARGS: ppdev (I) - pointer to physical device object
// pgp (I) - array of glyphs to render
// cGlyph (I) - number of glyphs to render
// ulCharInc (I) - fixed character spacing increment (0 if proportional font)
// RETN: TRUE if glyphs were rendered
//---------------------------------------------------------------------------------------------
// Renders an array of proportional or monospaced glyphs. This function requires RasterizerMode
// to be set-up to correctly byteswap and mirror bitmasks.
// NB. currently render to cxGlyphAligned rather than cxGlyph, this saves a lot of work on the
// host but probably costs, on average, four bits per glyph row; as this is a fallback
// routine I've not investigated whether this method is optimal.
//*********************************************************************************************
BOOL bPxRxUncachedText(PDEV* ppdev, GLYPHPOS* pgp, LONG cGlyph, ULONG ulCharInc)
{
GLYPHBITS *pgb;
LONG cxGlyph, cyGlyph, cxGlyphAligned;
LONG x, y;
ULONG *pulGlyph;
LONG cjGlyph;
LONG culGlyph;
LONG cjGlyphRem;
LONG cj;
ULONG ul;
GLINT_DECL;
DISPDBG((7, "bPxRxUncachedText: entered"));
if (ulCharInc)
{
x = pgp->ptl.x + pgp->pgdf->pgb->ptlOrigin.x - ulCharInc;
y = pgp->ptl.y + pgp->pgdf->pgb->ptlOrigin.y;
}
for ( ; --cGlyph >= 0; ++pgp)
{
pgb = pgp->pgdf->pgb;
if (ulCharInc)
{
x += ulCharInc;
}
else
{
x = pgp->ptl.x + pgb->ptlOrigin.x;
y = pgp->ptl.y + pgb->ptlOrigin.y;
}
cyGlyph = pgb->sizlBitmap.cy;
cxGlyph = pgb->sizlBitmap.cx;
cxGlyphAligned = ((cxGlyph + 7 ) & ~7);
// Render2D turns on FastFillEnable which is incompatible with PackedBitMasks
WAIT_PXRX_DMA_TAGS(4);
QUEUE_PXRX_DMA_TAG( __GlintTagRectanglePosition, MAKEDWORD_XY(x, y));
QUEUE_PXRX_DMA_TAG( __GlintTagRender2D, __RENDER2D_INCX | __RENDER2D_INCY |
__RENDER2D_OP_SYNCBITMASK |
__RENDER2D_WIDTH(cxGlyphAligned) |
__RENDER2D_HEIGHT(0));
QUEUE_PXRX_DMA_TAG(__GlintTagCount, cyGlyph);
QUEUE_PXRX_DMA_TAG(__GlintTagRender, __RENDER_TRAPEZOID_PRIMITIVE |
__RENDER_SYNC_ON_BIT_MASK);
pulGlyph = (ULONG *)pgb->aj;
cjGlyph = (cxGlyphAligned >> 3) * cyGlyph;
culGlyph = cjGlyph >> 2;
cjGlyphRem = cjGlyph & 3;
ul = culGlyph + (cjGlyphRem != 0);
WAIT_PXRX_DMA_DWORDS(ul + 1);
QUEUE_PXRX_DMA_HOLD(__GlintTagBitMaskPattern, ul);
for ( ; --culGlyph >= 0; ++pulGlyph)
{
QUEUE_PXRX_DMA_DWORD(*pulGlyph);
}
if (cjGlyphRem)
{
for (ul = cj = 0; cj < cjGlyphRem; ++cj, ++(BYTE *)pulGlyph)
{
ul |= ((ULONG)(*(BYTE *)pulGlyph)) << (cj << 3);
}
QUEUE_PXRX_DMA_DWORD(ul);
}
}
// The rasterizer's set-up to expect a continue after each Render command (NB. but not Render2D, etc),
// so it won't flush the text to the framebuffer unless we specifically tell it to
WAIT_PXRX_DMA_TAGS(1);
QUEUE_PXRX_DMA_TAG(__GlintTagContinueNewSub, 0);
DISPDBG((7, "bPxRxUncachedText: exited"));
return(TRUE);
}
//*********************************************************************************************
// FUNC: bPxRxUncachedClippedText
// ARGS: ppdev (I) - pointer to physical device object
// pgp (I) - array of glyphs to render
// cGlyph (I) - number of glyphs to render
// ulCharInc (I) - fixed character spacing increment (0 if proportional font)
// pco (I) - pointer to the clip region object
// RETN: TRUE if glyphs were rendered
//---------------------------------------------------------------------------------------------
// Renders an array of proportional or monospaced glyphs. This function requires RasterizerMode
// to be set-up to correctly byteswap and mirror bitmasks.
// NB. currently render to cxGlyphAligned rather than cxGlyph, this saves a lot of work on the
// host but probably costs, on average, four bits per glyph row; as this is a fallback
// routine I've not investigated whether this method is optimal.
//*********************************************************************************************
BOOL bPxRxUncachedClippedText(PDEV* ppdev, GLYPHPOS* pgp, LONG cGlyph, ULONG ulCharInc, CLIPOBJ *pco)
{
GLYPHBITS *pgb;
LONG cxGlyph, cyGlyph, cxGlyphAligned;
LONG x, y;
ULONG *pulGlyph;
LONG cjGlyph;
LONG culGlyph;
LONG cjGlyphRem;
LONG cj;
ULONG ul;
LONG cGlyphOriginal = 0;
GLYPHPOS *pgpOriginal = NULL;
BOOL bMore, invalidatedScissor = FALSE;
CLIPENUM ce;
RECTL *prclClip;
BOOL bClipSet;
GLINT_DECL;
DISPDBG((7, "bPxRxUncachedClippedText: entered"));
if (pco->iDComplexity == DC_RECT)
{
bMore = FALSE;
ce.c = 1;
prclClip = &pco->rclBounds;
goto SingleRectangle;
}
cGlyphOriginal = cGlyph;
pgpOriginal = pgp;
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++)
{
cGlyph = cGlyphOriginal;
pgp = pgpOriginal;
SingleRectangle:
bClipSet = FALSE;
if (ulCharInc)
{
x = pgp->ptl.x + pgp->pgdf->pgb->ptlOrigin.x - ulCharInc;
y = pgp->ptl.y + pgp->pgdf->pgb->ptlOrigin.y;
}
for ( ; --cGlyph >= 0; ++pgp)
{
pgb = pgp->pgdf->pgb;
if(ulCharInc)
{
x += ulCharInc;
}
else
{
x = pgp->ptl.x + pgb->ptlOrigin.x;
y = pgp->ptl.y + pgb->ptlOrigin.y;
}
cyGlyph = pgb->sizlBitmap.cy;
cxGlyph = pgb->sizlBitmap.cx;
cxGlyphAligned = ((cxGlyph + 7 ) & ~7);
if ((prclClip->right > x) && (prclClip->bottom > y) &&
(prclClip->left < x + cxGlyph) && (prclClip->top < y + cyGlyph))
{
// Lazily set the hardware clipping:
if(!bClipSet)
{
WAIT_PXRX_DMA_TAGS(3);
QUEUE_PXRX_DMA_TAG(__GlintTagScissorMinXY, (prclClip->top << SCISSOR_YOFFSET) |
(prclClip->left << SCISSOR_XOFFSET));
QUEUE_PXRX_DMA_TAG(__GlintTagScissorMaxXY, (prclClip->bottom << SCISSOR_YOFFSET) |
(prclClip->right << SCISSOR_XOFFSET));
QUEUE_PXRX_DMA_TAG(__GlintTagScissorMode, (USER_SCISSOR_ENABLE | SCREEN_SCISSOR_DEFAULT));
invalidatedScissor = TRUE;
bClipSet = TRUE;
}
// Render2D turns on FastFillEnable which is incompatible with PackedBitMasks
WAIT_PXRX_DMA_TAGS(4);
QUEUE_PXRX_DMA_TAG(__GlintTagRectanglePosition, MAKEDWORD_XY(x, y));
QUEUE_PXRX_DMA_TAG(__GlintTagRender2D, __RENDER2D_INCX |
__RENDER2D_INCY |
__RENDER2D_OP_SYNCBITMASK |
__RENDER2D_WIDTH(cxGlyphAligned) |
__RENDER2D_HEIGHT(0));
QUEUE_PXRX_DMA_TAG(__GlintTagCount, cyGlyph);
QUEUE_PXRX_DMA_TAG(__GlintTagRender, __RENDER_TRAPEZOID_PRIMITIVE |
__RENDER_SYNC_ON_BIT_MASK);
pulGlyph = (ULONG *)pgb->aj;
cjGlyph = (cxGlyphAligned >> 3) * cyGlyph;
culGlyph = cjGlyph >> 2;
cjGlyphRem = cjGlyph & 3;
ul = culGlyph + (cjGlyphRem != 0);
WAIT_PXRX_DMA_DWORDS(ul + 1);
QUEUE_PXRX_DMA_HOLD(__GlintTagBitMaskPattern, ul);
for ( ; --culGlyph >= 0; ++pulGlyph)
{
QUEUE_PXRX_DMA_DWORD(*pulGlyph);
}
if (cjGlyphRem)
{
for (ul = cj = 0; cj < cjGlyphRem; ++cj, ++(BYTE *)pulGlyph)
{
ul |= ((ULONG)(*(BYTE *)pulGlyph)) << (cj << 3);
}
QUEUE_PXRX_DMA_DWORD(ul);
}
}
}
}
} while(bMore);
// reset clipping
if (invalidatedScissor)
{
glintInfo->config2D |= __CONFIG2D_USERSCISSOR;
WAIT_PXRX_DMA_TAGS(1);
QUEUE_PXRX_DMA_TAG(__GlintTagScissorMaxXY, 0x7FFF7FFF);
}
// The rasterizer's set-up to expect a continue after each Render command (NB. but not Render2D, etc),
// so it won't flush the text to the framebuffer unless we specifically tell it to
WAIT_PXRX_DMA_TAGS(1);
QUEUE_PXRX_DMA_TAG(__GlintTagContinueNewSub, 0);
DISPDBG((7, "bPxRxUncachedClippedText: exited"));
return(TRUE);
}
//*********************************************************************************************
// FUNC: DrvTextOut
// ARGS: pso (I) - pointer to surface object to render to
// pstro (I) - pointer to the string object to be rendered
// pfo (I) - pointer to the font object
// pco (I) - pointer to the clip region object
// prclExtra (I) - 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 these).
// prclOpaque (I) - pointer to the opaque background rectangle
// pboFore (I) - pointer to the foreground brush object
// pboOpaque (I) - pointer to the brush for the opaque background rectangle
// pptlBrush (I) - pointer to the brush origin, Always unused, unless
// GCAPS_ARBRUSHOPAQUE set
// mix (I) - should always be a COPY operation
// RETN: TRUE - pstro glyphs have been rendered
//---------------------------------------------------------------------------------------------
// GDI calls this function when it has strings it wants us to render: this function should be
// exported in 'enable.c'.
//*********************************************************************************************
BOOL DrvTextOut(SURFOBJ* pso, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclExtra,
RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque, POINTL* pptlBrush,
MIX mix)
{
PDEV* ppdev;
LONG xOff;
DSURF* pdsurf;
OH* poh;
ULONG renderBits;
ULONG ulColor;
ULONG cGlyph;
BOOL bMoreGlyphs;
GLYPHPOS* pgp;
BYTE iDComplexity;
RECTL rclOpaque;
BOOL bRet = TRUE;
GLINT_DECL_VARS;
pdsurf = (DSURF*) pso->dhsurf;
ppdev = (PDEV*) pso->dhpdev;
REMOVE_SWPOINTER(pso);
if (pdsurf->dt & DT_SCREEN)
{
GLINT_DECL_INIT;
SETUP_PPDEV_OFFSETS(ppdev, pdsurf);
xOff = ppdev->xOffset = pdsurf->poh->x;
VALIDATE_DD_CONTEXT;
DISPDBG((9, "DrvTextOut: ppdev = %p pso->dhsurf->dt == %d", ppdev, pdsurf->dt));
// 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");
if (glintInfo->WriteMask != 0xffffffff)
{
// the texture unit requires all 32bpp of pixel data, so if we've got the upper
// 8 bits masked out for overlays we need to reenable these bits temporarily
WAIT_PXRX_DMA_TAGS(1);
glintInfo->WriteMask = 0xffffffff;
QUEUE_PXRX_DMA_TAG(__GlintTagFBHardwareWriteMask, 0xffffffff);
}
iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
if (prclOpaque != NULL)
{
int x,y,cx,cy;
RBRUSH_COLOR rbc;
////////////////////////////////////////////////////////////
// Opaque Initialization
////////////////////////////////////////////////////////////
if (iDComplexity == DC_TRIVIAL)
{
DrawOpaqueRect:
rbc.iSolidColor = pboOpaque->iSolidColor;
ppdev->pgfnFillSolid(ppdev, 1, prclOpaque, __GLINT_LOGICOP_COPY, __GLINT_LOGICOP_COPY, rbc, NULL);
}
else if (iDComplexity == DC_RECT)
{
DISPDBG((7, "DrvTextOut: drawing opaquing rect with rectangular clipping"));
if (bIntersect(prclOpaque, &pco->rclBounds, &rclOpaque))
{
prclOpaque = &rclOpaque;
goto DrawOpaqueRect;
}
}
else
{
// vPxRxClipSolid modifies the rect list we pass in but prclOpaque
// is probably a GDI structure so don't change it. This is also
// necessary for multi-headed drivers.
RECTL tmpOpaque = *prclOpaque;
DISPDBG((7, "DrvTextOut: drawing opaquing rect with complex clipping"));
vPxRxClipSolid(ppdev, 1, &tmpOpaque, pboOpaque->iSolidColor, pco);
}
}
if (prclOpaque == NULL)
{
// opaque initialization would have ensured the registers were correctly
// set up for a solid fill, without it we'll need to perform our own
// initialization.
SET_WRITE_BUFFERS;
WAIT_PXRX_DMA_TAGS(1);
LOAD_CONFIG2D(__CONFIG2D_CONSTANTSRC | __CONFIG2D_FBWRITE);
}
////////////////////////////////////////////////////////////
// Transparent Initialization
////////////////////////////////////////////////////////////
ulColor = pboFore->iSolidColor;
WAIT_PXRX_DMA_TAGS(1);
LOAD_FOREGROUNDCOLOUR( ulColor );
STROBJ_vEnumStart(pstro);
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
{
// never get here in WinBench97 business graphics
bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyph, &pgp);
}
if (cGlyph > 0)
{
// fall back to uncached rendering
if (iDComplexity == DC_TRIVIAL)
{
bRet = bPxRxUncachedText(ppdev, pgp, cGlyph, pstro->ulCharInc);
}
else
{
bRet = bPxRxUncachedClippedText(ppdev, pgp, cGlyph, pstro->ulCharInc, pco);
}
}
} while (bMoreGlyphs && bRet);
if(glintInfo->DefaultWriteMask != 0xffffffff)
{
WAIT_PXRX_DMA_TAGS(1);
glintInfo->WriteMask = glintInfo->DefaultWriteMask;
QUEUE_PXRX_DMA_TAG(__GlintTagFBHardwareWriteMask, glintInfo->DefaultWriteMask);
}
SEND_PXRX_DMA_QUERY;
}
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));
}
DISPDBG((9, "DrvTextOut: exiting"));
return(bRet);
}