/******************************Module*Header*******************************\ * Module Name: textout.c * * Copyright (c) 1992-1995 Microsoft Corporation * \**************************************************************************/ #include "precomp.h" BYTE gajBit[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; // Converts bit index to set bit /******************************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******************************\ * CACHEDFONT* pcfAllocateCachedFont() * * Initializes our font data structure. * \**************************************************************************/ CACHEDFONT* pcfAllocateCachedFont( PDEV* ppdev) { CACHEDFONT* pcf; CACHEDGLYPH** ppcg; LONG i; pcf = AtiAllocMem(LPTR, FL_ZERO_MEMORY, sizeof(CACHEDFONT)); if (pcf != NULL) { // Note that we rely on FL_ZERO_MEMORY to zero 'pgaChain' and // 'cjAlloc': pcf->cgSentinel.hg = HGLYPH_SENTINEL; // Initialize the hash table entries to all point to our sentinel: for (ppcg = &pcf->apcg[0], i = GLYPH_HASH_SIZE; i != 0; i--, ppcg++) { *ppcg = &pcf->cgSentinel; } } return(pcf); } /******************************Public*Routine******************************\ * VOID vFreeCachedFont() * * Frees all memory associated with the cache we kept for this font. * \**************************************************************************/ VOID vFreeCachedFont( CACHEDFONT* pcf) { GLYPHALLOC* pga; GLYPHALLOC* pgaNext; pga = pcf->pgaChain; while (pga != NULL) { pgaNext = pga->pgaNext; AtiFreeMem(pga); pga = pgaNext; } AtiFreeMem(pcf); } /******************************Public*Routine******************************\ * VOID vTrimAndPackGlyph * \**************************************************************************/ // expandto3 - expand one (monochrome) byte to three (24bpp) bytes, while // flipping the bits backwards. #define expandto3(a,b) \ { \ if ((a) & 0x80) *(b) |= 0x07; \ if ((a) & 0x40) *(b) |= 0x38; \ if ((a) & 0x20) {*(b) |= 0xC0; *((b)+1) |= 0x01;} \ if ((a) & 0x10) *((b)+1) |= 0x0E; \ if ((a) & 0x08) *((b)+1) |= 0x70; \ if ((a) & 0x04) {*((b)+1) |= 0x80; *((b)+2) |= 0x03;} \ if ((a) & 0x02) *((b)+2) |= 0x1C; \ if ((a) & 0x01) *((b)+2) |= 0xE0; \ } VOID vTrimAndPackGlyph( PDEV* ppdev, 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 // // N.B.: The glyph bits are packed in pjBuf backwards. // Failure to understand this cost me nearly a week's effort, // and gave me a whopping migraine. (This was for 24bpp.) if (ppdev->iBitmapFormat != BMF_24BPP) { 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++; } } } else { BYTE cur_byte, last_byte, last_byte2, next_byte; cjSrcWidth = (cxGlyph + cAlign + 7) >> 3; lSrcSkip = lDelta - cjSrcWidth; lDstSkip = (((cxGlyph + 7) >> 3) - cjSrcWidth - 1) * 3; 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, (3 * 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 bytes precedes the glyph bits array: pjDst -= 3; ASSERTDD((*pjSrc >> cAlign) == 0, "Trimmed off too many bits"); } cur_byte = last_byte = 0; 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 three bytes: jSrc = *pjSrc; cur_byte |= (jSrc >> (cAlign)); expandto3(cur_byte, pjDst); next_byte = (jSrc << (8 - cAlign)); expandto3(next_byte, pjDst+3); pjSrc++; pjDst += 3; last_byte2 = last_byte; last_byte = cur_byte; cur_byte = next_byte; } pjSrc += lSrcSkip; pjDst += lDstSkip; // can be -3 or -6 (if cAlign is big enough) !! cAlign += cRem; cur_byte = (lDstSkip != -3)? last_byte2:last_byte; if (cAlign >= 8) { cAlign -= 8; pjDst += 3; cur_byte = (lDstSkip != -3)? last_byte:next_byte; } } cxGlyph *= 3; } /////////////////////////////////////////////////////////////// // Return results *pcxGlyph = cxGlyph; *pcyGlyph = cyGlyph; *pptlOrigin = ptlOrigin; } /******************************Public*Routine******************************\ * VOID vPutGlyphInCache * * 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. * \**************************************************************************/ VOID vPutGlyphInCache( PDEV* ppdev, CACHEDGLYPH* pcg, GLYPHBITS* pgb) { BYTE* pjGlyph; LONG cxGlyph; LONG cyGlyph; POINTL ptlOrigin; pjGlyph = pgb->aj; cyGlyph = pgb->sizlBitmap.cy; cxGlyph = pgb->sizlBitmap.cx; ptlOrigin = pgb->ptlOrigin; vTrimAndPackGlyph(ppdev, (BYTE*) &pcg->ad, pjGlyph, &cxGlyph, &cyGlyph, &ptlOrigin); /////////////////////////////////////////////////////////////// // Initialize the glyph fields pcg->ptlOrigin = ptlOrigin; pcg->cx = cxGlyph; pcg->cy = cyGlyph; pcg->cxy = pcg->cy | (pcg->cx << 16); pcg->cw = (cxGlyph * cyGlyph + 15) >> 4; pcg->cd = (pcg->cw + 1) >> 1; } /******************************Public*Routine******************************\ * CACHEDGLYPH* pcgNew() * * Creates a new CACHEDGLYPH structure for keeping track of the glyph in * off-screen memory. bPutGlyphInCache is called to actually put the glyph * in off-screen memory. * * This routine should be reasonably device-independent, as bPutGlyphInCache * will contain most of the code that will have to be modified for other * display adapters. * \**************************************************************************/ CACHEDGLYPH* pcgNew( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp) { GLYPHBITS* pgb; GLYPHALLOC* pga; CACHEDGLYPH* pcg; LONG cjCachedGlyph; HGLYPH hg; LONG iHash; CACHEDGLYPH* pcgFind; // First, calculate the amount of storage we'll need for this glyph: pgb = pgp->pgdf->pgb; if (ppdev->iBitmapFormat != BMF_24BPP) { cjCachedGlyph = sizeof(CACHEDGLYPH) + ((pgb->sizlBitmap.cx * pgb->sizlBitmap.cy + 7) >> 3); // Reserve an extra byte at the end for temporary usage by our pack // routine: cjCachedGlyph++; } else { cjCachedGlyph = sizeof(CACHEDGLYPH) + ((3 * pgb->sizlBitmap.cx * pgb->sizlBitmap.cy + 7) >> 3); // Reserve 3 extra bytes at the end for temporary usage by our pack // routine: cjCachedGlyph += 3; } // We need to dword align it too: cjCachedGlyph = (cjCachedGlyph + 3) & ~3L; if (cjCachedGlyph > pcf->cjAlloc) { // Have to allocate a new glyph allocation structure: pga = AtiAllocMem(LPTR, FL_ZERO_MEMORY, GLYPH_ALLOC_SIZE); if (pga == NULL) { // It's safe to return at this time because we haven't // fatally altered any of our data structures: return(NULL); } // Add this allocation to the front of the allocation linked list, // so that we can free it later: pga->pgaNext = pcf->pgaChain; pcf->pgaChain = pga; // Now we've got a chunk of memory where we can store our cached // glyphs: pcf->pcgNew = &pga->acg[0]; pcf->cjAlloc = GLYPH_ALLOC_SIZE - (sizeof(*pga) - sizeof(pga->acg[0])); // It would be bad if we let in any glyphs that would be bigger // than our basic allocation size: ASSERTDD(cjCachedGlyph <= GLYPH_ALLOC_SIZE, "Woah, this is one big glyph!"); } pcg = pcf->pcgNew; // 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; } vPutGlyphInCache(ppdev, pcg, pgp->pgdf->pgb); return(pcg); } /******************************Public*Routine******************************\ * BOOL bI32CachedProportionalText * * Draws proportionally spaced glyphs via glyph caching. * \**************************************************************************/ BOOL bI32CachedProportionalText( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph) { BYTE* pjIoBase; HGLYPH hg; CACHEDGLYPH* pcg; LONG xOffset; LONG yOffset; LONG x; LONG y; LONG cw; WORD* pw; pjIoBase = ppdev->pjIoBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; do { 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) return(FALSE); } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: if (pcg->cx != 0) { // The glyph's origin y-coordinate may often be negative, so we // can't compute this as follows: // // x = pgp->ptl.x + pcg->ptlOrigin.x; // y = pgp->ptl.y + pcg->ptlOrigin.y; ASSERTDD((pgp->ptl.y + pcg->ptlOrigin.y) >= 0, "Can't have negative 'y' coordinates here"); I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 5); x = xOffset + pgp->ptl.x + pcg->ptlOrigin.x; I32_OW(pjIoBase, CUR_X, x); I32_OW(pjIoBase, DEST_X_START, x); I32_OW(pjIoBase, DEST_X_END, x + pcg->cx); y = yOffset + pgp->ptl.y + pcg->ptlOrigin.y; I32_OW(pjIoBase, CUR_Y, y); I32_OW(pjIoBase, DEST_Y_END, y + pcg->cy); // Take advantage of wait-stated I/O: pw = (WORD*) &pcg->ad[0]; cw = pcg->cw; I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 10); do { I32_OW_DIRECT(pjIoBase, PIX_TRANS, *pw); } while (pw++, --cw != 0); } } while (pgp++, --cGlyph != 0); return(TRUE); } /******************************Public*Routine******************************\ * BOOL bI32CachedFixedText * * Draws fixed spaced glyphs via glyph caching. * \*************************************************************************/ BOOL bI32CachedFixedText( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph, ULONG ulCharInc) { BYTE* pjIoBase; LONG xGlyph; LONG yGlyph; HGLYPH hg; CACHEDGLYPH* pcg; LONG x; LONG y; WORD* pw; LONG cw; pjIoBase = ppdev->pjIoBase; // Convert to absolute coordinates: xGlyph = pgp->ptl.x + ppdev->xOffset; yGlyph = pgp->ptl.y + ppdev->yOffset; do { 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) return(FALSE); } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: if (pcg->cx != 0) { x = xGlyph + pcg->ptlOrigin.x; y = yGlyph + pcg->ptlOrigin.y; I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 5); I32_OW(pjIoBase, CUR_X, x); I32_OW(pjIoBase, DEST_X_START, x); I32_OW(pjIoBase, DEST_X_END, x + pcg->cx); I32_OW(pjIoBase, CUR_Y, y); I32_OW(pjIoBase, DEST_Y_END, y + pcg->cy); // Take advantage of wait-stated I/O: pw = (WORD*) &pcg->ad[0]; cw = pcg->cw; I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 10); do { I32_OW_DIRECT(pjIoBase, PIX_TRANS, *pw); } while (pw++, --cw != 0); } xGlyph += ulCharInc; } while (pgp++, --cGlyph != 0); return(TRUE); } /******************************Public*Routine******************************\ * BOOL bI32CachedClippedText * * Draws clipped text via glyph caching. * \**************************************************************************/ BOOL bI32CachedClippedText( PDEV* ppdev, CACHEDFONT* pcf, STROBJ* pstro, CLIPOBJ* pco) { BOOL bRet; BYTE* pjIoBase; LONG xOffset; LONG yOffset; BOOL bMoreGlyphs; ULONG cGlyphOriginal; ULONG cGlyph; BOOL bClippingSet; GLYPHPOS* pgpOriginal; GLYPHPOS* pgp; LONG xGlyph; LONG yGlyph; LONG x; LONG y; LONG xRight; LONG yBottom; LONG cy; BOOL bMore; CLIPENUM ce; RECTL* prclClip; ULONG ulCharInc; HGLYPH hg; CACHEDGLYPH* pcg; WORD* pw; LONG cw; ASSERTDD((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL), "Don't expect trivial clipping in this function"); bRet = TRUE; pjIoBase = ppdev->pjIoBase; 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: bClippingSet = FALSE; pgp = pgpOriginal; cGlyph = cGlyphOriginal; // We can't yet convert to absolute coordinates by adding // in 'xOffset' or 'yOffset' here because we have yet to // compare the coordinates to 'prclClip': xGlyph = pgp->ptl.x; yGlyph = pgp->ptl.y; // Loop through all the glyphs for this rectangle: while (TRUE) { hg = pgp->hg; pcg = pcf->apcg[GLYPH_HASH_FUNC(hg)]; while (pcg->hg < hg) pcg = pcg->pcgNext; if (pcg->hg > hg) { // This will hopefully not be the common case (that is, // we will have a high cache hit rate), so if I were // writing this in Asm I would have this out-of-line // to avoid the jump around for the common case. // But the Pentium has branch prediction, so what the // heck. pcg = pcgNew(ppdev, pcf, pgp); if (pcg == NULL) { bRet = FALSE; goto AllDone; } } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: cy = pcg->cy; if (cy != 0) { y = pcg->ptlOrigin.y + yGlyph; x = pcg->ptlOrigin.x + xGlyph; xRight = pcg->cx + x; yBottom = pcg->cy + y; // Do trivial rejection: if ((prclClip->right > x) && (prclClip->bottom > y) && (prclClip->left < xRight) && (prclClip->top < yBottom)) { // Lazily set the hardware clipping: if (!bClippingSet) { bClippingSet = TRUE; vSetClipping(ppdev, prclClip); } I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 5); I32_OW(pjIoBase, CUR_X, xOffset + x); I32_OW(pjIoBase, DEST_X_START, xOffset + x); I32_OW(pjIoBase, DEST_X_END, xOffset + xRight); I32_OW(pjIoBase, CUR_Y, yOffset + y); I32_OW(pjIoBase, DEST_Y_END, yOffset + yBottom); I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 10); pw = (WORD*) &pcg->ad[0]; cw = pcg->cw; do { I32_OW_DIRECT(pjIoBase, PIX_TRANS, *pw); } while (pw++, --cw != 0); } } 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); } VOID vI32DataPortOutB(PDEV *ppdev, PBYTE pb, UINT count) { BYTE *pjIoBase = ppdev->pjIoBase; UINT i; for (i=0; i < count; i++) { if (i % 8 == 0) I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 10); I32_OB(pjIoBase, PIX_TRANS + 1, *((PUCHAR)pb)++); } } /******************************Public*Routine******************************\ * BOOL bI32GeneralText * \**************************************************************************/ BOOL bI32GeneralText( PDEV* ppdev, STROBJ* pstro, CLIPOBJ* pco) { BYTE* pjIoBase; 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 xBiasL = 0; LONG xBiasR = 0; LONG yBiasT = 0; LONG cy = 0; LONG cx = 0; BYTE* pjGlyph; LONG xLeft; LONG yTop; LONG xRight; LONG yBottom; RECTL NoClip; LONG x; LONG y; pjIoBase = ppdev->pjIoBase; /* Define Default Clipping area to be full video ram */ NoClip.top = 0; NoClip.left = 0; NoClip.right = ppdev->cxScreen; NoClip.bottom = ppdev->cyScreen; if (pco == NULL) iDComplexity = DC_TRIVIAL; else iDComplexity = 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 = &NoClip; 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; vSetClipping(ppdev, prclClip); //ppdev->lRightScissor = rclRealClip.right; ??? // Loop through all the glyphs for this rectangle: while (TRUE) { cxGlyph = pgb->sizlBitmap.cx; cyGlyph = pgb->sizlBitmap.cy; pjGlyph = (BYTE*) pgb->aj; if ((prclClip->left <= ptlOrigin.x) && (prclClip->top <= ptlOrigin.y) && (prclClip->right >= ptlOrigin.x + cxGlyph) && (prclClip->bottom >= ptlOrigin.y + cyGlyph)) { //----------------------------------------------------- // Unclipped glyph I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 6); x = ppdev->xOffset + ptlOrigin.x; I32_OW(pjIoBase, CUR_X, LOWORD(x)); I32_OW(pjIoBase, DEST_X_START, LOWORD(x)); I32_OW(pjIoBase, DEST_X_END, LOWORD(x) + ROUND8(cxGlyph) ); I32_OW(pjIoBase, SCISSOR_R, LOWORD(x) + cxGlyph-1); y = ppdev->yOffset + ptlOrigin.y; I32_OW(pjIoBase, CUR_Y, LOWORD(y)); I32_OW(pjIoBase, DEST_Y_END, (LOWORD(y) + cyGlyph)); vI32DataPortOutB(ppdev, pjGlyph, (ROUND8(cxGlyph) * cyGlyph) >> 3); /* _vBlit_DSC_SH1UP(ppdev,ptlOrigin.x, ptlOrigin.y, cxGlyph, cyGlyph, pjGlyph, (ROUND8(cxGlyph) * cyGlyph) >> 3); */ } 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 ( ( ptlOrigin.x <= prclClip->left ) && (ppdev->pModeInfo->ModeFlags & AMI_TEXTBAND) ) { vResetClipping(ppdev); return FALSE; } if (((cx = xRight - xLeft) > 0) && ((cy = yBottom - yTop) > 0)) { /* Do software clipping */ /* Calculated the Bias in pixels */ yBiasT = (yTop - ptlOrigin.y); /* change address of pjGlyph to point +yBiasT scan lines into the Glyph */ pjGlyph += (yBiasT * (ROUND8(cxGlyph) >> 3)); I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 6); x = ppdev->xOffset + ptlOrigin.x; I32_OW(pjIoBase, CUR_X, LOWORD(x)); I32_OW(pjIoBase, DEST_X_START, LOWORD(x)); I32_OW(pjIoBase, DEST_X_END, LOWORD(x) + ROUND8(cxGlyph) ); I32_OW(pjIoBase, SCISSOR_R, LOWORD(x) + cxGlyph-1); y = ppdev->yOffset + ptlOrigin.y; I32_OW(pjIoBase, CUR_Y, LOWORD(y+yBiasT)); I32_OW(pjIoBase, DEST_Y_END, (LOWORD(y+yBiasT) + cy)); vI32DataPortOutB(ppdev, pjGlyph, (ROUND8(cxGlyph) >> 3) * cy); /* _vBlit_DSC_SH1UP(ppdev,ptlOrigin.x,ptlOrigin.y+yBiasT, cxGlyph, cy, pjGlyph, (ROUND8(cxGlyph) >>3) * cy); */ } /*if*/ } 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); vResetClipping(ppdev); return TRUE; } /******************************Public*Routine******************************\ * BOOL bI32TextOut * \**************************************************************************/ BOOL bI32TextOut( PDEV* ppdev, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque) { BYTE* pjIoBase; LONG xOffset; LONG yOffset; ULONG cGlyph; BOOL bMoreGlyphs; GLYPHPOS* pgp; BYTE iDComplexity; CACHEDFONT* pcf; RECTL rclOpaque; BOOL bTextPerfectFit; pjIoBase = ppdev->pjIoBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity; if (prclOpaque != NULL) { //////////////////////////////////////////////////////////// // Opaque Initialization //////////////////////////////////////////////////////////// if (iDComplexity == DC_TRIVIAL) { DrawOpaqueRect: I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 8); I32_OW(pjIoBase, FRGD_COLOR, pboOpaque->iSolidColor); I32_OW(pjIoBase, ALU_FG_FN, OVERPAINT); I32_OW(pjIoBase, DP_CONFIG, FG_COLOR_SRC_FG | WRITE | DRAW); I32_OW(pjIoBase, CUR_X, xOffset + prclOpaque->left); I32_OW(pjIoBase, DEST_X_START, xOffset + prclOpaque->left); I32_OW(pjIoBase, DEST_X_END, xOffset + prclOpaque->right); I32_OW(pjIoBase, CUR_Y, yOffset + prclOpaque->top); vI32QuietDown(ppdev, pjIoBase); I32_OW(pjIoBase, DEST_Y_END, yOffset + prclOpaque->bottom); } else if (iDComplexity == DC_RECT) { if (bIntersect(prclOpaque, &pco->rclBounds, &rclOpaque)) { prclOpaque = &rclOpaque; goto DrawOpaqueRect; } } else { vClipSolid(ppdev, prclOpaque, pboOpaque->iSolidColor, pco); } 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) { I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 5); I32_OW(pjIoBase, ALU_BG_FN, OVERPAINT); I32_OW(pjIoBase, BKGD_COLOR, pboOpaque->iSolidColor); goto SkipTransparentInitialization; } } //////////////////////////////////////////////////////////// // Transparent Initialization //////////////////////////////////////////////////////////// I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 4); I32_OW(pjIoBase, ALU_BG_FN, LEAVE_ALONE); SkipTransparentInitialization: I32_OW(pjIoBase, DP_CONFIG, EXT_MONO_SRC_HOST | DRAW | WRITE | FG_COLOR_SRC_FG | BG_COLOR_SRC_BG | LSB_FIRST | BIT16); I32_OW(pjIoBase, ALU_FG_FN, OVERPAINT); I32_OW(pjIoBase, FRGD_COLOR, pboFore->iSolidColor); if ((pfo->cxMax <= GLYPH_CACHE_CX) && ((pstro->rclBkGround.bottom - pstro->rclBkGround.top) <= GLYPH_CACHE_CY)) { pcf = (CACHEDFONT*) pfo->pvConsumer; if (pcf == NULL) { pcf = pcfAllocateCachedFont(ppdev); if (pcf == NULL) return(FALSE); pfo->pvConsumer = pcf; } // Use our glyph cache: if (iDComplexity == DC_TRIVIAL) { 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 (!bI32CachedProportionalText(ppdev, pcf, pgp, cGlyph)) return(FALSE); } else { if (!bI32CachedFixedText(ppdev, pcf, pgp, cGlyph, pstro->ulCharInc)) return(FALSE); } } } while (bMoreGlyphs); } else { if (!bI32CachedClippedText(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)); I32_CHECK_FIFO_SPACE(ppdev, pjIoBase, 1); I32_OW(pjIoBase, DP_CONFIG, EXT_MONO_SRC_HOST | DRAW | WRITE | FG_COLOR_SRC_FG | BG_COLOR_SRC_BG); return bI32GeneralText(ppdev, pstro, pco); } return(TRUE); } /******************************Public*Routine******************************\ * BOOL bM32CachedProportionalText * * Draws proportionally spaced glyphs via glyph caching. * \**************************************************************************/ BOOL bM32CachedProportionalText( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph) { BYTE* pjMmBase; HGLYPH hg; CACHEDGLYPH* pcg; LONG xOffset; LONG yOffset; LONG x; LONG y; LONG cw; WORD* pw; pjMmBase = ppdev->pjMmBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; do { 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) return(FALSE); } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: if (pcg->cx != 0) { // The glyph's origin y-coordinate may often be negative, so we // can't compute this as follows: // // x = pgp->ptl.x + pcg->ptlOrigin.x; // y = pgp->ptl.y + pcg->ptlOrigin.y; ASSERTDD((pgp->ptl.y + pcg->ptlOrigin.y) >= 0, "Can't have negative 'y' coordinates here"); M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 5); x = xOffset + pgp->ptl.x + pcg->ptlOrigin.x; M32_OW(pjMmBase, CUR_X, x); M32_OW(pjMmBase, DEST_X_START, x); M32_OW(pjMmBase, DEST_X_END, x + pcg->cx); y = yOffset + pgp->ptl.y + pcg->ptlOrigin.y; M32_OW(pjMmBase, CUR_Y, y); M32_OW(pjMmBase, DEST_Y_END, y + pcg->cy); // Take advantage of wait-stated I/O: pw = (WORD*) &pcg->ad[0]; cw = pcg->cw; M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 10); do { M32_OW_DIRECT(pjMmBase, PIX_TRANS, *pw); } while (pw++, --cw != 0); } } while (pgp++, --cGlyph != 0); return(TRUE); } /******************************Public*Routine******************************\ * BOOL bM32CachedFixedText * * Draws fixed spaced glyphs via glyph caching. * \*************************************************************************/ BOOL bM32CachedFixedText( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph, ULONG ulCharInc) { BYTE* pjMmBase; LONG xGlyph; LONG yGlyph; HGLYPH hg; CACHEDGLYPH* pcg; LONG x; LONG y; WORD* pw; LONG cw; pjMmBase = ppdev->pjMmBase; // Convert to absolute coordinates: xGlyph = pgp->ptl.x + ppdev->xOffset; yGlyph = pgp->ptl.y + ppdev->yOffset; do { 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) return(FALSE); } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: if (pcg->cx != 0) { x = xGlyph + pcg->ptlOrigin.x; y = yGlyph + pcg->ptlOrigin.y; M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 5); M32_OW(pjMmBase, CUR_X, x); M32_OW(pjMmBase, DEST_X_START, x); M32_OW(pjMmBase, DEST_X_END, x + pcg->cx); M32_OW(pjMmBase, CUR_Y, y); M32_OW(pjMmBase, DEST_Y_END, y + pcg->cy); // Take advantage of wait-stated I/O: pw = (WORD*) &pcg->ad[0]; cw = pcg->cw; M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 10); do { M32_OW_DIRECT(pjMmBase, PIX_TRANS, *pw); } while (pw++, --cw != 0); } xGlyph += ulCharInc; } while (pgp++, --cGlyph != 0); return(TRUE); } /******************************Public*Routine******************************\ * BOOL bM32CachedClippedText * * Draws clipped text via glyph caching. * \**************************************************************************/ BOOL bM32CachedClippedText( PDEV* ppdev, CACHEDFONT* pcf, STROBJ* pstro, CLIPOBJ* pco) { BOOL bRet; BYTE* pjMmBase; LONG xOffset; LONG yOffset; BOOL bMoreGlyphs; ULONG cGlyphOriginal; ULONG cGlyph; BOOL bClippingSet; GLYPHPOS* pgpOriginal; GLYPHPOS* pgp; LONG xGlyph; LONG yGlyph; LONG x; LONG y; LONG xRight; LONG yBottom; LONG cy; BOOL bMore; CLIPENUM ce; RECTL* prclClip; ULONG ulCharInc; HGLYPH hg; CACHEDGLYPH* pcg; WORD* pw; LONG cw; ASSERTDD((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL), "Don't expect trivial clipping in this function"); bRet = TRUE; pjMmBase = ppdev->pjMmBase; 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: bClippingSet = FALSE; pgp = pgpOriginal; cGlyph = cGlyphOriginal; // We can't yet convert to absolute coordinates by adding // in 'xOffset' or 'yOffset' here because we have yet to // compare the coordinates to 'prclClip': xGlyph = pgp->ptl.x; yGlyph = pgp->ptl.y; // Loop through all the glyphs for this rectangle: while (TRUE) { hg = pgp->hg; pcg = pcf->apcg[GLYPH_HASH_FUNC(hg)]; while (pcg->hg < hg) pcg = pcg->pcgNext; if (pcg->hg > hg) { // This will hopefully not be the common case (that is, // we will have a high cache hit rate), so if I were // writing this in Asm I would have this out-of-line // to avoid the jump around for the common case. // But the Pentium has branch prediction, so what the // heck. pcg = pcgNew(ppdev, pcf, pgp); if (pcg == NULL) { bRet = FALSE; goto AllDone; } } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: cy = pcg->cy; if (cy != 0) { y = pcg->ptlOrigin.y + yGlyph; x = pcg->ptlOrigin.x + xGlyph; xRight = pcg->cx + x; yBottom = pcg->cy + y; // Do trivial rejection: if ((prclClip->right > x) && (prclClip->bottom > y) && (prclClip->left < xRight) && (prclClip->top < yBottom)) { // Lazily set the hardware clipping: if (!bClippingSet) { bClippingSet = TRUE; vSetClipping(ppdev, prclClip); } M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 5); M32_OW(pjMmBase, CUR_X, xOffset + x); M32_OW(pjMmBase, DEST_X_START, xOffset + x); M32_OW(pjMmBase, DEST_X_END, xOffset + xRight); M32_OW(pjMmBase, CUR_Y, yOffset + y); M32_OW(pjMmBase, DEST_Y_END, yOffset + yBottom); M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 10); pw = (WORD*) &pcg->ad[0]; cw = pcg->cw; do { M32_OW_DIRECT(pjMmBase, PIX_TRANS, *pw); } while (pw++, --cw != 0); } } 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); } VOID vM32DataPortOutB(PDEV *ppdev, PBYTE pb, UINT count) { BYTE *pjMmBase = ppdev->pjMmBase; UINT i; for (i=0; i < count; i++) { if (i % 8 == 0) M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 10); M32_OB(pjMmBase, PIX_TRANS + 1, *((PUCHAR)pb)++); } } /******************************Public*Routine******************************\ * BOOL bM32GeneralText * \**************************************************************************/ BOOL bM32GeneralText( PDEV* ppdev, STROBJ* pstro, CLIPOBJ* pco) { BYTE* pjMmBase; 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 xBiasL = 0; LONG xBiasR = 0; LONG yBiasT = 0; LONG cy = 0; LONG cx = 0; BYTE* pjGlyph; LONG xLeft; LONG yTop; LONG xRight; LONG yBottom; RECTL NoClip; LONG x; LONG y; pjMmBase = ppdev->pjMmBase; /* Define Default Clipping area to be full video ram */ NoClip.top = 0; NoClip.left = 0; NoClip.right = ppdev->cxScreen; NoClip.bottom = ppdev->cyScreen; if (pco == NULL) iDComplexity = DC_TRIVIAL; else iDComplexity = 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 = &NoClip; 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; vSetClipping(ppdev, prclClip); //ppdev->lRightScissor = rclRealClip.right; ??? // Loop through all the glyphs for this rectangle: while (TRUE) { cxGlyph = pgb->sizlBitmap.cx; cyGlyph = pgb->sizlBitmap.cy; pjGlyph = (BYTE*) pgb->aj; if ((prclClip->left <= ptlOrigin.x) && (prclClip->top <= ptlOrigin.y) && (prclClip->right >= ptlOrigin.x + cxGlyph) && (prclClip->bottom >= ptlOrigin.y + cyGlyph)) { //----------------------------------------------------- // Unclipped glyph M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 6); x = ppdev->xOffset + ptlOrigin.x; M32_OW(pjMmBase, CUR_X, LOWORD(x)); M32_OW(pjMmBase, DEST_X_START, LOWORD(x)); M32_OW(pjMmBase, DEST_X_END, LOWORD(x) + ROUND8(cxGlyph) ); M32_OW(pjMmBase, SCISSOR_R, LOWORD(x) + cxGlyph-1); y = ppdev->yOffset + ptlOrigin.y; M32_OW(pjMmBase, CUR_Y, LOWORD(y)); M32_OW(pjMmBase, DEST_Y_END, (LOWORD(y) + cyGlyph)); vM32DataPortOutB(ppdev, pjGlyph, (ROUND8(cxGlyph) * cyGlyph) >> 3); /* _vBlit_DSC_SH1UP(ppdev,ptlOrigin.x, ptlOrigin.y, cxGlyph, cyGlyph, pjGlyph, (ROUND8(cxGlyph) * cyGlyph) >> 3); */ } 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 ( ( ptlOrigin.x <= prclClip->left ) && (ppdev->pModeInfo->ModeFlags & AMI_TEXTBAND) ) { vResetClipping(ppdev); return FALSE; } if (((cx = xRight - xLeft) > 0) && ((cy = yBottom - yTop) > 0)) { /* Do software clipping */ /* Calculated the Bias in pixels */ yBiasT = (yTop - ptlOrigin.y); /* change address of pjGlyph to point +yBiasT scan lines into the Glyph */ pjGlyph += (yBiasT * (ROUND8(cxGlyph) >> 3)); M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 6); x = ppdev->xOffset + ptlOrigin.x; M32_OW(pjMmBase, CUR_X, LOWORD(x)); M32_OW(pjMmBase, DEST_X_START, LOWORD(x)); M32_OW(pjMmBase, DEST_X_END, LOWORD(x) + ROUND8(cxGlyph) ); M32_OW(pjMmBase, SCISSOR_R, LOWORD(x) + cxGlyph-1); y = ppdev->yOffset + ptlOrigin.y; M32_OW(pjMmBase, CUR_Y, LOWORD(y+yBiasT)); M32_OW(pjMmBase, DEST_Y_END, (LOWORD(y+yBiasT) + cy)); vM32DataPortOutB(ppdev, pjGlyph, (ROUND8(cxGlyph) >> 3) * cy); /* _vBlit_DSC_SH1UP(ppdev,ptlOrigin.x,ptlOrigin.y+yBiasT, cxGlyph, cy, pjGlyph, (ROUND8(cxGlyph) >>3) * cy); */ } /*if*/ } 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); vResetClipping(ppdev); return TRUE; } /******************************Public*Routine******************************\ * BOOL bM32TextOut * \**************************************************************************/ BOOL bM32TextOut( PDEV* ppdev, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque) { BYTE* pjMmBase; LONG xOffset; LONG yOffset; ULONG cGlyph; BOOL bMoreGlyphs; GLYPHPOS* pgp; BYTE iDComplexity; CACHEDFONT* pcf; RECTL rclOpaque; BOOL bTextPerfectFit; pjMmBase = ppdev->pjMmBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity; if (prclOpaque != NULL) { //////////////////////////////////////////////////////////// // Opaque Initialization //////////////////////////////////////////////////////////// if (iDComplexity == DC_TRIVIAL) { DrawOpaqueRect: M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 8); M32_OW(pjMmBase, FRGD_COLOR, pboOpaque->iSolidColor); M32_OW(pjMmBase, ALU_FG_FN, OVERPAINT); M32_OW(pjMmBase, DP_CONFIG, FG_COLOR_SRC_FG | WRITE | DRAW); M32_OW(pjMmBase, CUR_X, xOffset + prclOpaque->left); M32_OW(pjMmBase, DEST_X_START, xOffset + prclOpaque->left); M32_OW(pjMmBase, DEST_X_END, xOffset + prclOpaque->right); M32_OW(pjMmBase, CUR_Y, yOffset + prclOpaque->top); vM32QuietDown(ppdev, pjMmBase); M32_OW(pjMmBase, DEST_Y_END, yOffset + prclOpaque->bottom); } else if (iDComplexity == DC_RECT) { if (bIntersect(prclOpaque, &pco->rclBounds, &rclOpaque)) { prclOpaque = &rclOpaque; goto DrawOpaqueRect; } } else { vClipSolid(ppdev, prclOpaque, pboOpaque->iSolidColor, pco); } 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) { M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 5); M32_OW(pjMmBase, ALU_BG_FN, OVERPAINT); M32_OW(pjMmBase, BKGD_COLOR, pboOpaque->iSolidColor); goto SkipTransparentInitialization; } } //////////////////////////////////////////////////////////// // Transparent Initialization //////////////////////////////////////////////////////////// M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 4); M32_OW(pjMmBase, ALU_BG_FN, LEAVE_ALONE); SkipTransparentInitialization: M32_OW(pjMmBase, DP_CONFIG, EXT_MONO_SRC_HOST | DRAW | WRITE | FG_COLOR_SRC_FG | BG_COLOR_SRC_BG | LSB_FIRST | BIT16); M32_OW(pjMmBase, ALU_FG_FN, OVERPAINT); M32_OW(pjMmBase, FRGD_COLOR, pboFore->iSolidColor); if ((pfo->cxMax <= GLYPH_CACHE_CX) && ((pstro->rclBkGround.bottom - pstro->rclBkGround.top) <= GLYPH_CACHE_CY)) { pcf = (CACHEDFONT*) pfo->pvConsumer; if (pcf == NULL) { pcf = pcfAllocateCachedFont(ppdev); if (pcf == NULL) return(FALSE); pfo->pvConsumer = pcf; } // Use our glyph cache: if (iDComplexity == DC_TRIVIAL) { 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 (!bM32CachedProportionalText(ppdev, pcf, pgp, cGlyph)) return(FALSE); } else { if (!bM32CachedFixedText(ppdev, pcf, pgp, cGlyph, pstro->ulCharInc)) return(FALSE); } } } while (bMoreGlyphs); } else { if (!bM32CachedClippedText(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)); M32_CHECK_FIFO_SPACE(ppdev, pjMmBase, 1); M32_OW(pjMmBase, DP_CONFIG, EXT_MONO_SRC_HOST | DRAW | WRITE | FG_COLOR_SRC_FG | BG_COLOR_SRC_BG); return bM32GeneralText(ppdev, pstro, pco); } return(TRUE); } /******************************Public*Routine******************************\ * BOOL bM64CachedProportionalText * * Draws proportionally spaced glyphs via glyph caching. * \**************************************************************************/ BOOL bM64CachedProportionalText( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph) { BYTE* pjMmBase; HGLYPH hg; CACHEDGLYPH* pcg; LONG xOffset; LONG yOffset; LONG x; LONG y; LONG cd; DWORD* pd; LONG cFifo; pjMmBase = ppdev->pjMmBase; 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; } if (pcg->hg > hg) { // This will hopefully not be the common case (that is, // we will have a high cache hit rate), so if I were // writing this in Asm I would have this out-of-line // to avoid the jump around for the common case. // But the Pentium has branch prediction, so what the // heck. pcg = pcgNew(ppdev, pcf, pgp); if (pcg == NULL) return(FALSE); } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: if (pcg->cx != 0) { // The glyph's origin y-coordinate may often be negative, so we // can't compute this as follows: // // x = pgp->ptl.x + pcg->ptlOrigin.x; // y = pgp->ptl.y + pcg->ptlOrigin.y; ASSERTDD((pgp->ptl.y + pcg->ptlOrigin.y) >= 0, "Can't have negative 'y' coordinates here"); cFifo -= 2; if (cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 14; } x = xOffset + pgp->ptl.x + pcg->ptlOrigin.x; y = yOffset + pgp->ptl.y + pcg->ptlOrigin.y; M64_OD(pjMmBase, DST_Y_X, PACKXY_FAST(x, y)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, pcg->cxy); pd = (DWORD*) &pcg->ad[0]; cd = pcg->cd; do { if (--cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 15; } M64_OD(pjMmBase, HOST_DATA0, *pd); } while (pd++, --cd != 0); } } while (pgp++, --cGlyph != 0); return(TRUE); } BOOL bM64CachedProportionalText24( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph) { BYTE* pjMmBase; HGLYPH hg; CACHEDGLYPH* pcg; LONG xOffset; LONG yOffset; LONG x; LONG y; LONG cd; DWORD* pd; LONG cFifo; pjMmBase = ppdev->pjMmBase; 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; } if (pcg->hg > hg) { // This will hopefully not be the common case (that is, // we will have a high cache hit rate), so if I were // writing this in Asm I would have this out-of-line // to avoid the jump around for the common case. // But the Pentium has branch prediction, so what the // heck. pcg = pcgNew(ppdev, pcf, pgp); if (pcg == NULL) return(FALSE); } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: if (pcg->cx != 0) { // The glyph's origin y-coordinate may often be negative, so we // can't compute this as follows: // // x = pgp->ptl.x + pcg->ptlOrigin.x; // y = pgp->ptl.y + pcg->ptlOrigin.y; ASSERTDD((pgp->ptl.y + pcg->ptlOrigin.y) >= 0, "Can't have negative 'y' coordinates here"); cFifo -= 3; if (cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 13; } x = (xOffset + pgp->ptl.x + pcg->ptlOrigin.x) * 3; y = yOffset + pgp->ptl.y + pcg->ptlOrigin.y; M64_OD(pjMmBase, DST_CNTL, 0x83 | (((x + MAX_NEGX*3)/4 % 6) << 8)); M64_OD(pjMmBase, DST_Y_X, PACKXY_FAST(x, y)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, pcg->cxy); pd = (DWORD*) &pcg->ad[0]; cd = pcg->cd; do { if (--cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 15; } M64_OD(pjMmBase, HOST_DATA0, *pd); } while (pd++, --cd != 0); } } while (pgp++, --cGlyph != 0); return(TRUE); } /******************************Public*Routine******************************\ * BOOL bM64CachedFixedText * * Draws fixed spaced glyphs via glyph caching. * \*************************************************************************/ BOOL bM64CachedFixedText( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph, ULONG ulCharInc) { BYTE* pjMmBase; LONG xGlyph; LONG yGlyph; HGLYPH hg; CACHEDGLYPH* pcg; LONG x; LONG y; DWORD* pd; LONG cd; LONG cFifo; pjMmBase = ppdev->pjMmBase; cFifo = 0; // Convert to absolute coordinates: xGlyph = pgp->ptl.x + ppdev->xOffset; yGlyph = pgp->ptl.y + ppdev->yOffset; do { 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) return(FALSE); } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: if (pcg->cx != 0) { x = xGlyph + pcg->ptlOrigin.x; y = yGlyph + pcg->ptlOrigin.y; cFifo -= 2; if (cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 14; } M64_OD(pjMmBase, DST_Y_X, PACKXY_FAST(x, y)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, pcg->cxy); pd = (DWORD*) &pcg->ad[0]; cd = pcg->cd; do { if (--cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 15; } M64_OD(pjMmBase, HOST_DATA0, *pd); } while (pd++, --cd != 0); } xGlyph += ulCharInc; } while (pgp++, --cGlyph != 0); return(TRUE); } BOOL bM64CachedFixedText24( PDEV* ppdev, CACHEDFONT* pcf, GLYPHPOS* pgp, LONG cGlyph, ULONG ulCharInc) { BYTE* pjMmBase; LONG xGlyph; LONG yGlyph; HGLYPH hg; CACHEDGLYPH* pcg; LONG x; LONG y; DWORD* pd; LONG cd; LONG cFifo; pjMmBase = ppdev->pjMmBase; cFifo = 0; // Convert to absolute coordinates: xGlyph = pgp->ptl.x + ppdev->xOffset; yGlyph = pgp->ptl.y + ppdev->yOffset; do { 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) return(FALSE); } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: if (pcg->cx != 0) { x = (xGlyph + pcg->ptlOrigin.x) * 3; y = yGlyph + pcg->ptlOrigin.y; cFifo -= 3; if (cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 13; } M64_OD(pjMmBase, DST_CNTL, 0x83 | (((x + MAX_NEGX*3)/4 % 6) << 8)); M64_OD(pjMmBase, DST_Y_X, PACKXY_FAST(x, y)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, pcg->cxy); pd = (DWORD*) &pcg->ad[0]; cd = pcg->cd; do { if (--cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 15; } M64_OD(pjMmBase, HOST_DATA0, *pd); } while (pd++, --cd != 0); } xGlyph += ulCharInc; } while (pgp++, --cGlyph != 0); return(TRUE); } /******************************Public*Routine******************************\ * BOOL bM64CachedClippedText * * Draws clipped text via glyph caching. * \**************************************************************************/ BOOL bM64CachedClippedText( PDEV* ppdev, CACHEDFONT* pcf, STROBJ* pstro, CLIPOBJ* pco) { BOOL bRet; BYTE* pjMmBase; LONG xOffset; LONG yOffset; BOOL bMoreGlyphs; ULONG cGlyphOriginal; ULONG cGlyph; BOOL bClippingSet; GLYPHPOS* pgpOriginal; GLYPHPOS* pgp; LONG xGlyph; LONG yGlyph; LONG x; LONG y; LONG xRight; LONG yBottom; LONG cy; BOOL bMore; CLIPENUM ce; RECTL* prclClip; ULONG ulCharInc; HGLYPH hg; CACHEDGLYPH* pcg; DWORD* pd; LONG cd; LONG cFifo; ASSERTDD((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL), "Don't expect trivial clipping in this function"); bRet = TRUE; pjMmBase = ppdev->pjMmBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; ulCharInc = pstro->ulCharInc; cFifo = 0; 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: bClippingSet = FALSE; pgp = pgpOriginal; cGlyph = cGlyphOriginal; // We can't yet convert to absolute coordinates by adding // in 'xOffset' or 'yOffset' here because we have yet to // compare the coordinates to 'prclClip': xGlyph = pgp->ptl.x; yGlyph = pgp->ptl.y; // Loop through all the glyphs for this rectangle: while (TRUE) { hg = pgp->hg; pcg = pcf->apcg[GLYPH_HASH_FUNC(hg)]; while (pcg->hg < hg) pcg = pcg->pcgNext; if (pcg->hg > hg) { // This will hopefully not be the common case (that is, // we will have a high cache hit rate), so if I were // writing this in Asm I would have this out-of-line // to avoid the jump around for the common case. // But the Pentium has branch prediction, so what the // heck. pcg = pcgNew(ppdev, pcf, pgp); if (pcg == NULL) { bRet = FALSE; goto AllDone; } } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: cy = pcg->cy; if (cy != 0) { y = pcg->ptlOrigin.y + yGlyph; x = pcg->ptlOrigin.x + xGlyph; xRight = pcg->cx + x; yBottom = pcg->cy + y; // Do trivial rejection: if ((prclClip->right > x) && (prclClip->bottom > y) && (prclClip->left < xRight) && (prclClip->top < yBottom)) { // Lazily set the hardware clipping: if (!bClippingSet) { bClippingSet = TRUE; vSetClipping(ppdev, prclClip); cFifo = 0; // Have to initialize count } cFifo -= 2; if (cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 14; } M64_OD(pjMmBase, DST_Y_X, PACKXY(xOffset + x, yOffset + y)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, pcg->cxy); pd = (DWORD*) &pcg->ad[0]; cd = pcg->cd; do { if (--cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 15; } M64_OD(pjMmBase, HOST_DATA0, *pd); } while (pd++, --cd != 0); } } 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); } BOOL bM64CachedClippedText24( PDEV* ppdev, CACHEDFONT* pcf, STROBJ* pstro, CLIPOBJ* pco) { BOOL bRet; BYTE* pjMmBase; LONG xOffset; LONG yOffset; BOOL bMoreGlyphs; ULONG cGlyphOriginal; ULONG cGlyph; BOOL bClippingSet; GLYPHPOS* pgpOriginal; GLYPHPOS* pgp; LONG xGlyph; LONG yGlyph; LONG x; LONG y; LONG xRight; LONG yBottom; LONG cy; BOOL bMore; CLIPENUM ce; RECTL* prclClip; ULONG ulCharInc; HGLYPH hg; CACHEDGLYPH* pcg; DWORD* pd; LONG cd; LONG cFifo; LONG xTmp; ASSERTDD((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL), "Don't expect trivial clipping in this function"); bRet = TRUE; pjMmBase = ppdev->pjMmBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; ulCharInc = pstro->ulCharInc; cFifo = 0; 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: bClippingSet = FALSE; pgp = pgpOriginal; cGlyph = cGlyphOriginal; // We can't yet convert to absolute coordinates by adding // in 'xOffset' or 'yOffset' here because we have yet to // compare the coordinates to 'prclClip': xGlyph = pgp->ptl.x; yGlyph = pgp->ptl.y; // Loop through all the glyphs for this rectangle: while (TRUE) { hg = pgp->hg; pcg = pcf->apcg[GLYPH_HASH_FUNC(hg)]; while (pcg->hg < hg) pcg = pcg->pcgNext; if (pcg->hg > hg) { // This will hopefully not be the common case (that is, // we will have a high cache hit rate), so if I were // writing this in Asm I would have this out-of-line // to avoid the jump around for the common case. // But the Pentium has branch prediction, so what the // heck. pcg = pcgNew(ppdev, pcf, pgp); if (pcg == NULL) { bRet = FALSE; goto AllDone; } } // Space glyphs are trimmed to a height of zero, and we don't // even have to touch the hardware for them: cy = pcg->cy; if (cy != 0) { y = pcg->ptlOrigin.y + yGlyph; x = pcg->ptlOrigin.x + xGlyph; xRight = pcg->cx + x; yBottom = pcg->cy + y; // Do trivial rejection: if ((prclClip->right > x) && (prclClip->bottom > y) && (prclClip->left < xRight) && (prclClip->top < yBottom)) { // Lazily set the hardware clipping: if (!bClippingSet) { bClippingSet = TRUE; vSetClipping(ppdev, prclClip); cFifo = 0; // Have to initialize count } cFifo -= 3; if (cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 13; } xTmp = (xOffset + x) * 3; M64_OD(pjMmBase, DST_CNTL, 0x83 | (((xTmp + MAX_NEGX*3)/4 % 6) << 8)); M64_OD(pjMmBase, DST_Y_X, PACKXY(xTmp, yOffset + y)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, pcg->cxy); pd = (DWORD*) &pcg->ad[0]; cd = pcg->cd; do { if (--cFifo < 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); cFifo = 15; } M64_OD(pjMmBase, HOST_DATA0, *pd); } while (pd++, --cd != 0); } } 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 bM64GeneralText * \**************************************************************************/ BOOL bM64GeneralText( PDEV* ppdev, STROBJ* pstro, CLIPOBJ* pco) { BYTE* pjMmBase; 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 xBiasL = 0; LONG xBiasR = 0; LONG yBiasT = 0; LONG cy = 0; LONG cx = 0; BYTE* pjGlyph; LONG xLeft; LONG yTop; LONG xRight; LONG yBottom; RECTL NoClip; pjMmBase = ppdev->pjMmBase; /* Define Default Clipping area to be full video ram */ NoClip.top = 0; NoClip.left = 0; NoClip.right = ppdev->cxScreen; NoClip.bottom = ppdev->cyScreen; if (pco == NULL) iDComplexity = DC_TRIVIAL; else iDComplexity = 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 = &NoClip; 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; vSetClipping(ppdev, prclClip); //ppdev->lRightScissor = rclRealClip.right; ??? // Loop through all the glyphs for this rectangle: while (TRUE) { cxGlyph = pgb->sizlBitmap.cx; cyGlyph = pgb->sizlBitmap.cy; pjGlyph = (BYTE*) pgb->aj; if ((prclClip->left <= ptlOrigin.x) && (prclClip->top <= ptlOrigin.y) && (prclClip->right >= ptlOrigin.x + cxGlyph) && (prclClip->bottom >= ptlOrigin.y + cyGlyph)) { //----------------------------------------------------- // Unclipped glyph M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 3); M64_OD(pjMmBase, HOST_CNTL, 1); M64_OD(pjMmBase, DST_Y_X, ((ppdev->yOffset+ptlOrigin.y) & 0xffff) | ((ppdev->xOffset+ptlOrigin.x) << 16)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, cyGlyph | cxGlyph << 16); vM64DataPortOutB(ppdev, pjGlyph, (ROUND8(cxGlyph) * cyGlyph) >> 3); /* _vBlit_DSC_SH1UP(ppdev,ptlOrigin.x, ptlOrigin.y, cxGlyph, cyGlyph, pjGlyph, (ROUND8(cxGlyph) * cyGlyph) >> 3); */ } 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 ( ( ptlOrigin.x <= prclClip->left ) && (ppdev->pModeInfo->ModeFlags & AMI_TEXTBAND) ) { vResetClipping(ppdev); return FALSE; } if (((cx = xRight - xLeft) > 0) && ((cy = yBottom - yTop) > 0)) { /* Do software clipping */ /* Calculated the Bias in pixels */ yBiasT = (yTop - ptlOrigin.y); /* change address of pjGlyph to point +yBiasT scan lines into the Glyph */ pjGlyph += (yBiasT * (ROUND8(cxGlyph) >> 3)); M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 3); M64_OD(pjMmBase, HOST_CNTL, 1); M64_OD(pjMmBase, DST_Y_X, ((ppdev->yOffset+ptlOrigin.y+yBiasT) & 0xffff) | ((ppdev->xOffset+ptlOrigin.x) << 16)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, cy | cxGlyph << 16); vM64DataPortOutB(ppdev, pjGlyph, (ROUND8(cxGlyph) >> 3) * cy); /* _vBlit_DSC_SH1UP(ppdev,ptlOrigin.x,ptlOrigin.y+yBiasT, cxGlyph, cy, pjGlyph, (ROUND8(cxGlyph) >>3) * cy); */ } /*if*/ } 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); vResetClipping(ppdev); // We must reset the HOST_CNTL register, or else BAD things happen when // rendering text in the OTHER functions. M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 1); M64_OD(pjMmBase, HOST_CNTL, 0); return TRUE; } VOID vM64DataPortOutD_24bppmono(PDEV* ppdev, PBYTE pb, UINT count, LONG pitch) { BYTE* pjMmBase = ppdev->pjMmBase; UINT i,j; DWORD hostdata, remainder; UINT l; LONG data24; unsigned char data8; hostdata = 0; l = 0; for (i = 0; i < count; i++) { switch (l) { case 0: // expand 8 to 24bpp data24 = 0; data8 = *pb++; for (j = 0; j < 8; j++) { data24 <<= 3; if ((data8 >> j) & 1) { data24 |= 7; } } hostdata = data24; // expand 8 to 24bpp data24 = 0; data8 = *pb++; for (j = 0; j < 8; j++) { data24 <<= 3; if ((data8 >> j) & 1) { data24 |= 7; } } remainder = data24; hostdata = hostdata | (remainder << 24); break; case 1: data24 = 0; data8 = *pb++; for (j = 0; j < 8; j++) { data24 <<= 3; if ((data8 >> j) & 1) { data24 |= 7; } } remainder = data24; hostdata = (hostdata >> 8) | (remainder << 16); break; case 2: data24 = 0; data8 = *pb++; for (j = 0; j < 8; j++) { data24 <<= 3; if ((data8 >> j) & 1) { data24 |= 7; } } remainder = data24; hostdata = (hostdata >> 16) | (remainder << 8); break; } if ((i % 14) == 0) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 16); } M64_OD(pjMmBase, HOST_DATA0, hostdata); hostdata = remainder; // 24 bpp alignment variable handling l = (l+1) % 3; } } BOOL bM64GeneralText24( PDEV* ppdev, STROBJ* pstro, CLIPOBJ* pco) { BYTE* pjMmBase; 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 xBiasL = 0; LONG xBiasR = 0; LONG yBiasT = 0; LONG cy = 0; LONG cx = 0; BYTE* pjGlyph; LONG xLeft; LONG yTop; LONG xRight; LONG yBottom; RECTL NoClip; BOOLEAN resetScissor; LONG x; DWORD dwCount; pjMmBase = ppdev->pjMmBase; /* Define Default Clipping area to be full video ram */ NoClip.top = 0; NoClip.left = 0; NoClip.right = ppdev->cxScreen; NoClip.bottom = ppdev->cyScreen; if (pco == NULL) iDComplexity = DC_TRIVIAL; else iDComplexity = 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 = &NoClip; 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; vSetClipping(ppdev, prclClip); //ppdev->lRightScissor = rclRealClip.right; ??? // Loop through all the glyphs for this rectangle: while (TRUE) { cxGlyph = pgb->sizlBitmap.cx; cyGlyph = pgb->sizlBitmap.cy; pjGlyph = (BYTE*) pgb->aj; if ((prclClip->left <= ptlOrigin.x) && (prclClip->top <= ptlOrigin.y) && (prclClip->right >= ptlOrigin.x + cxGlyph) && (prclClip->bottom >= ptlOrigin.y + cyGlyph)) { //----------------------------------------------------- // Unclipped glyph x = ppdev->xOffset+ptlOrigin.x; resetScissor = FALSE; if ((prclClip->right * 3) - 1 > (x - ppdev->xOffset + cxGlyph) * 3 - 1) { resetScissor = TRUE; M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 4); M64_OD(pjMmBase, SC_RIGHT, (x + cxGlyph) * 3 - 1); } else { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 3); } M64_OD(pjMmBase, DST_CNTL, 0x83 | (((x + MAX_NEGX)*3/4 % 6) << 8)); M64_OD(pjMmBase, DST_Y_X, ((ppdev->yOffset+ptlOrigin.y) & 0xffff) | (x*3 << 16)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, cyGlyph | (ROUND8(cxGlyph) * 3) << 16); dwCount = (ROUND8(cxGlyph) * 3 * cyGlyph + 31) / 32; vM64DataPortOutD_24bppmono(ppdev, pjGlyph, dwCount, cxGlyph); if (resetScissor) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 1); M64_OD(pjMmBase, SC_RIGHT, (ppdev->xOffset + prclClip->right) * 3 - 1); } } 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 ( ( ptlOrigin.x <= prclClip->left ) && (ppdev->pModeInfo->ModeFlags & AMI_TEXTBAND) ) { vResetClipping(ppdev); return FALSE; } if (((cx = xRight - xLeft) > 0) && ((cy = yBottom - yTop) > 0)) { /* Do software clipping */ /* Calculated the Bias in pixels */ yBiasT = (yTop - ptlOrigin.y); /* change address of pjGlyph to point +yBiasT scan lines into the Glyph */ pjGlyph += (yBiasT * (ROUND8(cxGlyph) >> 3)); x = ppdev->xOffset+ptlOrigin.x; resetScissor = FALSE; if ((prclClip->right * 3) - 1 > (x - ppdev->xOffset + cxGlyph) * 3 - 1) { resetScissor = TRUE; M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 4); M64_OD(pjMmBase, SC_RIGHT, (x + cxGlyph) * 3 - 1); } else { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 3); } M64_OD(pjMmBase, DST_CNTL, 0x83 | (((x + MAX_NEGX)*3/4 % 6) << 8)); M64_OD(pjMmBase, DST_Y_X, ((ppdev->yOffset+ptlOrigin.y+yBiasT) & 0xffff) | (x*3 << 16)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, cy | (ROUND8(cxGlyph) * 3) << 16); dwCount = (ROUND8(cxGlyph) * 3 * cy + 31) / 32; vM64DataPortOutD_24bppmono(ppdev, pjGlyph, dwCount, cxGlyph); if (resetScissor) { M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 1); M64_OD(pjMmBase, SC_RIGHT, (ppdev->xOffset + prclClip->right) * 3 - 1); } } /*if*/ } 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); vResetClipping(ppdev); return TRUE; } /******************************Public*Routine******************************\ * BOOL bM64TextOut * \**************************************************************************/ BOOL bM64TextOut( PDEV* ppdev, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque) { BYTE* pjMmBase; LONG xOffset; LONG yOffset; ULONG cGlyph; BOOL bMoreGlyphs; GLYPHPOS* pgp; BYTE iDComplexity; CACHEDFONT* pcf; RECTL rclOpaque; pjMmBase = ppdev->pjMmBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity; if (prclOpaque != NULL) { //////////////////////////////////////////////////////////// // Opaque Initialization //////////////////////////////////////////////////////////// if (iDComplexity == DC_TRIVIAL) { DrawOpaqueRect: M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 5); M64_OD(pjMmBase, DP_MIX, (OVERPAINT << 16)); M64_OD(pjMmBase, DP_FRGD_CLR, pboOpaque->iSolidColor); M64_OD(pjMmBase, DP_SRC, DP_SRC_FrgdClr << 8); M64_OD(pjMmBase, DST_Y_X, PACKXY_FAST(xOffset + prclOpaque->left, yOffset + prclOpaque->top)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, PACKXY_FAST(prclOpaque->right - prclOpaque->left, prclOpaque->bottom - prclOpaque->top)); } else if (iDComplexity == DC_RECT) { if (bIntersect(prclOpaque, &pco->rclBounds, &rclOpaque)) { prclOpaque = &rclOpaque; goto DrawOpaqueRect; } } else { vClipSolid(ppdev, prclOpaque, pboOpaque->iSolidColor, pco); } // I didn't observe any performance difference between setting // the ATI to opaque or transparent mode (when the font allowed // it -- some don't). } //////////////////////////////////////////////////////////// // Transparent Initialization //////////////////////////////////////////////////////////// M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 5); M64_OD(pjMmBase, CONTEXT_LOAD_CNTL, CONTEXT_LOAD_CmdLoad | ppdev->iDefContext ); M64_OD(pjMmBase, DP_MIX, (OVERPAINT << 16) | LEAVE_ALONE); M64_OD(pjMmBase, DP_FRGD_CLR, pboFore->iSolidColor); M64_OD(pjMmBase, DP_SRC, (DP_SRC_Host << 16) | (DP_SRC_FrgdClr << 8) | (DP_SRC_BkgdClr)); // For some reason, the SRC color depth must be monochrome. // Otherwise, it will cause wait-for-idle to hang. M64_OD(pjMmBase, DP_PIX_WIDTH, ppdev->ulMonoPixelWidth & 0xFFFF00FF); if ((pfo->cxMax <= GLYPH_CACHE_CX) && ((pstro->rclBkGround.bottom - pstro->rclBkGround.top) <= GLYPH_CACHE_CY)) { pcf = (CACHEDFONT*) pfo->pvConsumer; if (pcf == NULL) { pcf = pcfAllocateCachedFont(ppdev); if (pcf == NULL) goto ReturnFalse; 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 (!bM64CachedProportionalText(ppdev, pcf, pgp, cGlyph)) goto ReturnFalse; } else { if (!bM64CachedFixedText(ppdev, pcf, pgp, cGlyph, pstro->ulCharInc)) goto ReturnFalse; } } } while (bMoreGlyphs); } else { if (!bM64CachedClippedText(ppdev, pcf, pstro, pco)) goto ReturnFalse; } } else { DISPDBG((4, "Text too big to cache: %li x %li", pfo->cxMax, pstro->rclBkGround.bottom - pstro->rclBkGround.top)); if (!bM64GeneralText(ppdev, pstro, pco)) goto ReturnFalse; } M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 1); M64_OD(pjMmBase, DP_PIX_WIDTH, ppdev->ulMonoPixelWidth); return(TRUE); ReturnFalse: M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 1); M64_OD(pjMmBase, DP_PIX_WIDTH, ppdev->ulMonoPixelWidth); return(FALSE); } BOOL bM64TextOut24( PDEV* ppdev, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque) { BYTE* pjMmBase; LONG xOffset; LONG yOffset; ULONG cGlyph; BOOL bMoreGlyphs; GLYPHPOS* pgp; BYTE iDComplexity; CACHEDFONT* pcf; RECTL rclOpaque; LONG x; pjMmBase = ppdev->pjMmBase; xOffset = ppdev->xOffset; yOffset = ppdev->yOffset; iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity; if (prclOpaque != NULL) { //////////////////////////////////////////////////////////// // Opaque Initialization //////////////////////////////////////////////////////////// if (iDComplexity == DC_TRIVIAL) { DrawOpaqueRect: x = (xOffset + prclOpaque->left) * 3; M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 6); M64_OD(pjMmBase, DST_CNTL, 0x83 | ((x/4 % 6) << 8)); M64_OD(pjMmBase, DP_MIX, (OVERPAINT << 16)); M64_OD(pjMmBase, DP_FRGD_CLR, pboOpaque->iSolidColor); M64_OD(pjMmBase, DP_SRC, DP_SRC_FrgdClr << 8); M64_OD(pjMmBase, DST_Y_X, PACKXY_FAST(x, yOffset + prclOpaque->top)); M64_OD(pjMmBase, DST_HEIGHT_WIDTH, PACKXY_FAST((prclOpaque->right - prclOpaque->left) * 3, prclOpaque->bottom - prclOpaque->top)); } else if (iDComplexity == DC_RECT) { if (bIntersect(prclOpaque, &pco->rclBounds, &rclOpaque)) { prclOpaque = &rclOpaque; goto DrawOpaqueRect; } } else { vClipSolid(ppdev, prclOpaque, pboOpaque->iSolidColor, pco); } // I didn't observe any performance difference between setting // the ATI to opaque or transparent mode (when the font allowed // it -- some don't). } //////////////////////////////////////////////////////////// // Transparent Initialization //////////////////////////////////////////////////////////// M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 5); M64_OD(pjMmBase, CONTEXT_LOAD_CNTL, CONTEXT_LOAD_CmdLoad | ppdev->iDefContext ); M64_OD(pjMmBase, DP_MIX, (OVERPAINT << 16) | LEAVE_ALONE); M64_OD(pjMmBase, DP_FRGD_CLR, pboFore->iSolidColor); M64_OD(pjMmBase, DP_SRC, (DP_SRC_Host << 16) | (DP_SRC_FrgdClr << 8) | (DP_SRC_BkgdClr)); // For some reason, the SRC color depth must be monochrome. // Otherwise, it will cause wait-for-idle to hang. M64_OD(pjMmBase, DP_PIX_WIDTH, ppdev->ulMonoPixelWidth & 0xFFFF00FF); if ((pfo->cxMax <= GLYPH_CACHE_CX) && ((pstro->rclBkGround.bottom - pstro->rclBkGround.top) <= GLYPH_CACHE_CY)) { pcf = (CACHEDFONT*) pfo->pvConsumer; if (pcf == NULL) { pcf = pcfAllocateCachedFont(ppdev); if (pcf == NULL) goto ReturnFalse; 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 (!bM64CachedProportionalText24(ppdev, pcf, pgp, cGlyph)) goto ReturnFalse; } else { if (!bM64CachedFixedText24(ppdev, pcf, pgp, cGlyph, pstro->ulCharInc)) goto ReturnFalse; } } } while (bMoreGlyphs); } else { if (!bM64CachedClippedText24(ppdev, pcf, pstro, pco)) goto ReturnFalse; } } else { DISPDBG((4, "Text too big to cache: %li x %li", pfo->cxMax, pstro->rclBkGround.bottom - pstro->rclBkGround.top)); if (!bM64GeneralText24(ppdev, pstro, pco)) goto ReturnFalse; } M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 2); M64_OD(pjMmBase, DST_CNTL, DST_CNTL_XDir | DST_CNTL_YDir); M64_OD(pjMmBase, DP_PIX_WIDTH, ppdev->ulMonoPixelWidth); return(TRUE); ReturnFalse: M64_CHECK_FIFO_SPACE(ppdev, pjMmBase, 2); M64_OD(pjMmBase, DST_CNTL, DST_CNTL_XDir | DST_CNTL_YDir); M64_OD(pjMmBase, DP_PIX_WIDTH, ppdev->ulMonoPixelWidth); return(FALSE); } /******************************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; DSURF* pdsurf; OH* poh; // 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"); pdsurf = (DSURF*) pso->dhsurf; if (pdsurf->dt != DT_DIB) { poh = pdsurf->poh; ppdev = (PDEV*) pso->dhpdev; ppdev->xOffset = poh->x; ppdev->yOffset = poh->y; if (!ppdev->pfnTextOut(ppdev, pstro, pfo, pco, prclOpaque, pboFore, pboOpaque)) { if (DIRECT_ACCESS(ppdev)) { BANK bnk; vBankStart(ppdev, (prclOpaque != NULL) ? prclOpaque : &pstro->rclBkGround, pco, &bnk); do { EngTextOut(bnk.pso, pstro, pfo, bnk.pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlBrush, mix); } while (bBankEnum(&bnk)); } else { BOOL b; BYTE* pjBits; BYTE* pjScan0; HSURF hsurfDst; LONG lDelta; RECTL rclDst; RECTL rclScreen; SIZEL sizl; SURFOBJ* psoTmp; b = FALSE; // For error cases, assume we'll fail /* rclDst.left = 0; rclDst.top = 0; rclDst.right = pdsurf->sizl.cx; rclDst.bottom = pdsurf->sizl.cy; */ rclDst = (prclOpaque != NULL) ? *prclOpaque : pstro->rclBkGround; if ((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL)) { rclDst.left = max(rclDst.left, pco->rclBounds.left); rclDst.top = max(rclDst.top, pco->rclBounds.top); rclDst.right = min(rclDst.right, pco->rclBounds.right); rclDst.bottom = min(rclDst.bottom, pco->rclBounds.bottom); } sizl.cx = rclDst.right - rclDst.left; sizl.cy = rclDst.bottom - rclDst.top; { // We need to create a temporary work buffer. We have to do // some fudging with the offsets so that the upper-left corner // of the (relative coordinates) clip object bounds passed to // GDI will be transformed to the upper-left corner of our // temporary bitmap. // The alignment doesn't have to be as tight as this at 16bpp // and 32bpp, but it won't hurt: lDelta = (((rclDst.right + 3) & ~3L) - (rclDst.left & ~3L)) * ppdev->cjPelSize; // We're actually only allocating a bitmap that is 'sizl.cx' x // 'sizl.cy' in size: pjBits = AtiAllocMem(LMEM_FIXED, 0, lDelta * sizl.cy); if (pjBits == NULL) goto Error_2; // We now adjust the surface's 'pvScan0' so that when GDI thinks // it's writing to pixel (rclDst.top, rclDst.left), it will // actually be writing to the upper-left pixel of our temporary // bitmap: pjScan0 = pjBits - (rclDst.top * lDelta) - ((rclDst.left & ~3L) * ppdev->cjPelSize); ASSERTDD((((ULONG_PTR) pjScan0) & 3) == 0, "pvScan0 must be dword aligned!"); hsurfDst = (HSURF) EngCreateBitmap( sizl, // Bitmap covers rectangle lDelta, // Use this delta ppdev->iBitmapFormat, // Same colour depth BMF_TOPDOWN, // Must have a positive delta pjScan0); // Where (0, 0) would be if ((hsurfDst == 0) || (!EngAssociateSurface(hsurfDst, ppdev->hdevEng, 0))) goto Error_3; psoTmp = EngLockSurface(hsurfDst); if (psoTmp == NULL) goto Error_4; // Make sure that the rectangle we Get/Put from/to the screen // is in absolute coordinates: rclScreen.left = rclDst.left + ppdev->xOffset; rclScreen.right = rclDst.right + ppdev->xOffset; rclScreen.top = rclDst.top + ppdev->yOffset; rclScreen.bottom = rclDst.bottom + ppdev->yOffset; ppdev->pfnGetBits(ppdev, psoTmp, &rclDst, (POINTL*) &rclScreen); b = EngTextOut(psoTmp, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlBrush, mix); ppdev->pfnPutBits(ppdev, psoTmp, &rclScreen, (POINTL*) &rclDst); EngUnlockSurface(psoTmp); Error_4: EngDeleteSurface(hsurfDst); Error_3: AtiFreeMem(pjBits); } Error_2: return(b); } } } else { // We're drawing to a DFB we've converted to a DIB, so just call GDI // to handle it: return(EngTextOut(pdsurf->pso, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlBrush, mix)); } return(TRUE); } /******************************Public*Routine******************************\ * BOOL bEnableText * * Performs the necessary setup for the text drawing subcomponent. * \**************************************************************************/ BOOL bEnableText( PDEV* ppdev) { return(TRUE); } /******************************Public*Routine******************************\ * VOID vDisableText * * Performs the necessary clean-up for the text drawing subcomponent. * \**************************************************************************/ VOID vDisableText(PDEV* ppdev) { } /******************************Public*Routine******************************\ * VOID vAssertModeText * * Disables or re-enables the text drawing subcomponent in preparation for * full-screen entry/exit. * \**************************************************************************/ VOID vAssertModeText( PDEV* ppdev, BOOL bEnable) { // If we were to do off-screen glyph caching, we would probably want // to invalidate our cache here, because it will get destroyed when // we switch to full-screen. } /******************************Public*Routine******************************\ * VOID DrvDestroyFont * * We're being notified that the given font is being deallocated; clean up * anything we've stashed in the 'pvConsumer' field of the 'pfo'. * \**************************************************************************/ VOID DrvDestroyFont( FONTOBJ* pfo) { CACHEDFONT* pcf; pcf = pfo->pvConsumer; if (pcf != NULL) { vFreeCachedFont(pcf); pfo->pvConsumer = NULL; } }