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