/******************************Module*Header*******************************\ * Module Name: textgdi.cxx * * Text APIs for NT graphics engine * * Created: 18-Dec-1990 10:09:19 * Author: Donald Sidoroff [donalds] * * Copyright (c) 1990-1999 Microsoft Corporation \**************************************************************************/ #include "precomp.hxx" EFLOAT efCos(EFLOAT x); EFLOAT efSin(EFLOAT x); extern PBRUSH gpbrText; extern PBRUSH gpbrBackground; #define XFORMNULL (EXFORMOBJ *) NULL // This is a tiny class just to make sure we don't forget to free up any // objects before leaving ExtTextOut. class TXTCLEANUP { XDCOBJ *pdco; public: TXTCLEANUP() {pdco=(DCOBJ *) NULL;} ~TXTCLEANUP() {if (pdco != (DCOBJ *) NULL) vMopUp();} VOID vSet(XDCOBJ& dco) {pdco = &dco;} VOID vMopUp(); }; VOID TXTCLEANUP::vMopUp() { RGNOBJ ro(pdco->pdc->prgnAPI()); ro.bDeleteRGNOBJ(); pdco->pdc->prgnAPI(NULL); } // Helper routine to draw a device coordinate RECTL into a path. BOOL bAddRectToPath(EPATHOBJ& po,RECTL *prcl) { POINTFIX aptfx[4]; aptfx[3].x = aptfx[0].x = LTOFX(prcl->left); aptfx[1].y = aptfx[0].y = LTOFX(prcl->top); aptfx[2].x = aptfx[1].x = LTOFX(prcl->right); aptfx[3].y = aptfx[2].y = LTOFX(prcl->bottom); return(po.bAddPolygon(XFORMNULL,(POINTL *) aptfx,4)); } /******************************Public*Routine******************************\ * GreExtTextOutRect: Wrapper for common GreExtTextOutW code. This routine just * acquires locks then calls GreExtTextOutWLocked * * Arguments: * * Same as ExtTextOutRect * * Return Value: * * BOOL States * * History: * * 30-Oct-1995 - Initial version of wrapper * \**************************************************************************/ BOOL GreExtTextOutRect( HDC hdc, LPRECT prcl ) { BOOL bRet = FALSE; // // call GreExtTextOutW // XDCOBJ dcoDst(hdc); // // Validate the destination DC. // if (dcoDst.bValid()) { DEVLOCKOBJ dlo; if (dlo.bLock(dcoDst)) { if ((!dcoDst.bDisplay() || dcoDst.bRedirection()) || UserScreenAccessCheck()) { bRet = ExtTextOutRect( dcoDst, prcl); } else { SAVE_ERROR_CODE(ERROR_ACCESS_DENIED); } } else { bRet = dcoDst.bFullScreen(); } dcoDst.vUnlock(); } return(bRet); } /******************************Public*Routine******************************\ * ExtTextOutRect * * Called when just a Rectangle is passed. Over-used by Winbench4.0 the * perf app we aim to please. * * History: * 02-Nov-1993 -by- Patrick Haluptzok patrickh * Wrote it. \**************************************************************************/ BOOL ExtTextOutRect( XDCOBJ &dcoDst, LPRECT prcl ) { // Assume failure. BOOL bReturn; // Validate the destination DC. if (dcoDst.bValid()) { ASSERTGDI(prcl != NULL, "This API must be past a valid rect"); EXFORMOBJ xoDst(dcoDst, WORLD_TO_DEVICE); if (!xoDst.bRotation()) { ERECTL erclDst(prcl->left, prcl->top, prcl->right, prcl->bottom); xoDst.bXform(erclDst); erclDst.vOrder(); if (!erclDst.bEmpty()) { // Accumulate bounds. We can do this before knowing if the operation is // successful because bounds can be loose. if (dcoDst.fjAccum()) dcoDst.vAccumulate(erclDst); // Check surface is included in DC. SURFACE *pSurfDst = dcoDst.pSurface(); if (pSurfDst != NULL) { // With a fixed DC origin we can change the destination to SCREEN coordinates. erclDst += dcoDst.eptlOrigin(); ECLIPOBJ *pco = NULL; // This is a pretty knarly expression to save a return in here. // Basically pco can be NULL if the rect is completely in the // cached rect in the DC or if we set up a clip object that isn't empty. if (((erclDst.left >= dcoDst.prclClip()->left) && (erclDst.right <= dcoDst.prclClip()->right) && (erclDst.top >= dcoDst.prclClip()->top) && (erclDst.bottom <= dcoDst.prclClip()->bottom)) || (pco = dcoDst.pco(), pco->vSetup(dcoDst.prgnEffRao(), erclDst,CLIP_NOFORCETRIV), erclDst = pco->erclExclude(), (!erclDst.bEmpty()))) { // Set up the brush if necessary. EBRUSHOBJ *pbo = dcoDst.peboBackground(); if (dcoDst.bDirtyBrush(DIRTY_BACKGROUND)) { dcoDst.vCleanBrush(DIRTY_BACKGROUND); XEPALOBJ palDst(pSurfDst->ppal()); XEPALOBJ palDstDC(dcoDst.ppal()); pbo->vInitBrush(dcoDst.pdc, gpbrBackground, palDstDC, palDst, pSurfDst, (dcoDst.flGraphicsCaps() & GCAPS_ARBRUSHOPAQUE) ? TRUE : FALSE); } DEVEXCLUDEOBJ dxo(dcoDst,&erclDst,pco); // Inc the target surface uniqueness INC_SURF_UNIQ(pSurfDst); // Dispatch the call. bReturn = (*(pSurfDst->pfnBitBlt())) (pSurfDst->pSurfobj(), (SURFOBJ *) NULL, (SURFOBJ *) NULL, pco, NULL, &erclDst, (POINTL *) NULL, (POINTL *) NULL, pbo, &dcoDst.pdc->ptlFillOrigin(), 0x0000f0f0); } else { bReturn = TRUE; } } else { bReturn = TRUE; } } else { bReturn = TRUE; } } else { // There is rotation involved - send it off to ExtTextOutW to handle it. bReturn = GreExtTextOutWLocked(dcoDst, 0, 0, ETO_OPAQUE, prcl, (LPWSTR) NULL, 0, NULL, dcoDst.pdc->jBkMode(), NULL, 0); } } else { WARNING1("ERROR TextOutRect called on invalid DC\n"); bReturn = FALSE; } return(bReturn); } /******************************Public*Routine******************************\ * BOOL GrePolyTextOut * * Write text with lots of random spacing and alignment options. * * Arguments: * * hdc - handle to DC * lpto - Ptr to array of polytext structures * nStrings - number of polytext structures * * Return Value: * * History: * Tue 09-Nov-1993 -by- Patrick Haluptzok [patrickh] * For code size we'll call ExtTextOutW. After the RFONT and brushes are * realized, the RaoRegion is setup etc, future calls to ExtTextOutW * will be very cheap. Plus ExtTextOut will be in the working set where a * huge GrePolyTextOut won't be. It is still a savings by avoiding kernel * transitions * \**************************************************************************/ BOOL GrePolyTextOutW( HDC hdc, POLYTEXTW *lpto, UINT nStrings, DWORD dwCodePage ) { BYTE CaptureBuffer[TEXT_CAPTURE_BUFFER_SIZE]; BOOL bRet = TRUE; // // call GreExtTextOutW // XDCOBJ dcoDst(hdc); // // Validate the destination DC. // if (dcoDst.bValid()) { DEVLOCKOBJ dlo; if (dlo.bLock(dcoDst)) { for (POLYTEXTW *ppt = lpto; ppt < lpto+nStrings; ppt += 1) { // // Try to use stack buffer // PVOID pStrObjBuffer = NULL; ULONG cjStrObj = SIZEOF_STROBJ_BUFFER(ppt->n); if (TEXT_CAPTURE_BUFFER_SIZE >= cjStrObj) { pStrObjBuffer = CaptureBuffer; } if (!GreExtTextOutWLocked( dcoDst, ppt->x, ppt->y, ppt->uiFlags, &(ppt->rcl), (LPWSTR) ppt->lpstr, ppt->n, ppt->pdx, dcoDst.pdc->jBkMode(), pStrObjBuffer, dwCodePage)) { WARNING1("GrePolyTextOutW failed a call to GreExtTextOutW\n"); bRet = FALSE; break; } } } else { bRet = dcoDst.bFullScreen(); } dcoDst.vUnlock(); } else { WARNING1("GrePolyTextOutW: can not lock dc \n"); SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); bRet = FALSE; } return(bRet); } /******************************Public*Routine******************************\ * GreExtTextOutW: Wrapper for common GreExtTextOutW code. This routine just * acquires locks then calls GreExtTextOutWLocked * * Arguments: * * Same as GreExtTextOutW * * Return Value: * * BOOL States * * History: * * 30-Oct-1995 - Initial version of wrapper * \**************************************************************************/ // for user/kernel consumption only BOOL GreExtTextOutW( HDC hdc, int x, int y, UINT flOpts, CONST RECT *prcl, LPCWSTR pwsz, UINT cwc, CONST INT *pdx ) { return GreExtTextOutWInternal( hdc, x, y, flOpts, (LPRECT)prcl, (LPWSTR)pwsz, (int) cwc, (LPINT)pdx, NULL, 0 ); } BOOL GreExtTextOutWInternal( HDC hdc, int x, int y, UINT flOpts, LPRECT prcl, LPWSTR pwsz, int cwc, LPINT pdx, PVOID pvBuffer, DWORD dwCodePage ) { BOOL bRet = FALSE; // // call GreExtTextOutW // XDCOBJ dcoDst(hdc); // // Validate the destination DC. // if (dcoDst.bValid()) { DEVLOCKOBJ dlo; if (dlo.bLock(dcoDst)) { bRet = GreExtTextOutWLocked( dcoDst, x, y, flOpts, prcl, pwsz, cwc, pdx, dcoDst.pdc->jBkMode(), pvBuffer, dwCodePage); } else { bRet = dcoDst.bFullScreen(); } dcoDst.vUnlock(); } return(bRet); } /******************************Public*Routine******************************\ * GreBatchTextOut: Set propper DC flags and attributes, then call textout * pr textoutrect. * * Arguments: * * dcoDst Locked DCOBJ * xoDst XFORMOBJ * pbText Batched textout entry * * Return Value: * * Status * * 14-Oct-1995 -by- Mark Enstrom [marke] * \**************************************************************************/ #define ETO_NULL_PRCL 0x80000000 BOOL GreBatchTextOut( XDCOBJ &dcoDst, PBATCHTEXTOUT pbText, ULONG cjBatchLength // cannot trust the Length field in pbText // because it is accessible to user-mode ) { // // set foreground and background color in DC // and restore after call (if needed) // BYTE CaptureBuffer[TEXT_CAPTURE_BUFFER_SIZE]; COLORREF crSaveText = (COLORREF)-1; COLORREF crSaveBack = (COLORREF)-1; HANDLE hlfntNewSave = NULL; UINT flTextAlignSave = -1; POINTL ptlViewportOrgSave; PVOID pStrObjBuffer = NULL; ULONG ulSaveText, ulSaveBack; if (pbText->Type == BatchTypeTextOutRect) { ASSERTGDI(((PBATCHTEXTOUTRECT)pbText)->fl & ETO_OPAQUE, "GreBatchTextOut, flags\n"); crSaveBack = dcoDst.pdc->crBackClr(); ulSaveBack = dcoDst.pdc->ulBackClr(); if (crSaveBack != ((PBATCHTEXTOUTRECT)pbText)->BackColor) { dcoDst.pdc->crBackClr(((PBATCHTEXTOUTRECT)pbText)->BackColor); dcoDst.pdc->ulBackClr(((PBATCHTEXTOUTRECT)pbText)->ulBackColor); dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_BACKGROUND); } ptlViewportOrgSave = dcoDst.pdc->ptlViewportOrg(); if ((ptlViewportOrgSave.x != ((PBATCHTEXTOUTRECT)pbText)->ptlViewportOrg.x) || (ptlViewportOrgSave.y != ((PBATCHTEXTOUTRECT)pbText)->ptlViewportOrg.y)) { dcoDst.pdc->lViewportOrgX(((PBATCHTEXTOUTRECT)pbText)->ptlViewportOrg.x); dcoDst.pdc->lViewportOrgY(((PBATCHTEXTOUTRECT)pbText)->ptlViewportOrg.y); dcoDst.pdc->flSet_flXform( PAGE_XLATE_CHANGED | DEVICE_TO_WORLD_INVALID); } ExtTextOutRect(dcoDst,(LPRECT)&((PBATCHTEXTOUTRECT)pbText)->rcl); if (dcoDst.pdc->crBackClr() != crSaveBack) { dcoDst.pdc->crBackClr(crSaveBack); dcoDst.pdc->ulBackClr(ulSaveBack); dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_BACKGROUND); } if ((ptlViewportOrgSave.x != dcoDst.pdc->lViewportOrgX()) || (ptlViewportOrgSave.y != dcoDst.pdc->lViewportOrgY())) { dcoDst.pdc->lViewportOrgX(ptlViewportOrgSave.x); dcoDst.pdc->lViewportOrgY(ptlViewportOrgSave.y); dcoDst.pdc->flSet_flXform( PAGE_XLATE_CHANGED | DEVICE_TO_WORLD_INVALID); } } else { RECT newRect; LPRECT prcl; LPINT pdx = NULL; // // Capture the buffer length parameters from the TEB batch. // They can be overwritten by user-mode, so are not to be // trusted. // ULONG fl = pbText->fl; ULONG cChar = pbText->cChar; ULONG PdxOffset = pbText->PdxOffset; // // Check that char data will not overflow the data buffer. // ULONG cjBuf = cjBatchLength - offsetof(BATCHTEXTOUT, ulBuffer); if (cChar > (cjBuf / sizeof(WCHAR))) { WARNING("GreBatchTextOut: batch overflow char\n"); return FALSE; } // // Check that optional pdx data will not overflow the data buffer. // if (PdxOffset != 0) { ULONG cjPdxPerChar; // // The pdx array must have cChar number of horizontal // deltas and, if the optional ETO_PDY flag is set, cChar // number of vertical deltas. // cjPdxPerChar = sizeof(INT); if (fl & ETO_PDY) cjPdxPerChar *= 2; // // Is the start and end of the pdx array in range? // if ((PdxOffset > cjBuf) || (cChar > ((cjBuf - PdxOffset) / cjPdxPerChar))) { WARNING("GreBatchTextOut: batch overflow pdx\n"); return FALSE; } // // Since we know that the pdx array is in range, go ahead // and set pdx. // pdx = (LPINT)((PUCHAR)&pbText->ulBuffer[0] + PdxOffset); } // // Set foreground and background text colors. // crSaveText = dcoDst.pdc->crTextClr(); ulSaveText = dcoDst.pdc->ulTextClr(); if (crSaveText != pbText->TextColor) { dcoDst.pdc->crTextClr(pbText->TextColor); dcoDst.pdc->ulTextClr(pbText->ulTextColor); dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_TEXT); } crSaveBack = dcoDst.pdc->crBackClr(); ulSaveBack = dcoDst.pdc->ulBackClr(); if (crSaveBack != pbText->BackColor) { dcoDst.pdc->crBackClr(pbText->BackColor); dcoDst.pdc->ulBackClr(pbText->ulBackColor); dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_BACKGROUND); } if (dcoDst.pdc->hlfntNew() != pbText->hlfntNew) { hlfntNewSave = dcoDst.pdc->hlfntNew(); dcoDst.pdc->hlfntNew((HLFONT)pbText->hlfntNew); dcoDst.ulDirtyAdd(DIRTY_CHARSET); dcoDst.ulDirtySub(SLOW_WIDTHS); } if (dcoDst.pdc->flTextAlign() != pbText->flTextAlign) { flTextAlignSave = dcoDst.pdc->flTextAlign(); dcoDst.pdc->flTextAlign(pbText->flTextAlign); } ptlViewportOrgSave = dcoDst.pdc->ptlViewportOrg(); if ((ptlViewportOrgSave.x != pbText->ptlViewportOrg.x) || (ptlViewportOrgSave.y != pbText->ptlViewportOrg.y)) { dcoDst.pdc->lViewportOrgX(pbText->ptlViewportOrg.x); dcoDst.pdc->lViewportOrgY(pbText->ptlViewportOrg.y); dcoDst.pdc->flSet_flXform( PAGE_XLATE_CHANGED | DEVICE_TO_WORLD_INVALID); } if (fl & ETO_NULL_PRCL) { prcl = NULL; fl &= ~ETO_NULL_PRCL; } else { __try { newRect = ProbeAndReadStructure((LPRECT)&pbText->rcl,RECT); prcl = &newRect; } __except(EXCEPTION_EXECUTE_HANDLER) { WARNINGX(119); return FALSE; } } // // try to use stack based temp buffer // ULONG cjStrObj = SIZEOF_STROBJ_BUFFER(cChar); if (TEXT_CAPTURE_BUFFER_SIZE >= cjStrObj) { pStrObjBuffer = &CaptureBuffer[0]; } GreExtTextOutWLocked( dcoDst, pbText->x, pbText->y, fl, prcl, (LPWSTR)&pbText->ulBuffer[0], cChar, pdx, pbText->BackMode, pStrObjBuffer, pbText->dwCodePage); // // Restore the foreground and background text colors. // if (dcoDst.pdc->crTextClr() != crSaveText) { dcoDst.pdc->crTextClr(crSaveText); dcoDst.pdc->ulTextClr(ulSaveText); dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_TEXT); } if (dcoDst.pdc->crBackClr() != crSaveBack) { dcoDst.pdc->crBackClr(crSaveBack); dcoDst.pdc->ulBackClr(ulSaveBack); dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_BACKGROUND); } if (hlfntNewSave != NULL) { dcoDst.pdc->hlfntNew(((HLFONT)hlfntNewSave)); dcoDst.ulDirtyAdd(DIRTY_CHARSET); dcoDst.ulDirtySub(SLOW_WIDTHS); } if (flTextAlignSave != -1) { dcoDst.pdc->flTextAlign(flTextAlignSave); } if ((ptlViewportOrgSave.x != dcoDst.pdc->lViewportOrgX()) || (ptlViewportOrgSave.y != dcoDst.pdc->lViewportOrgY())) { dcoDst.pdc->lViewportOrgX(ptlViewportOrgSave.x); dcoDst.pdc->lViewportOrgY(ptlViewportOrgSave.y); dcoDst.pdc->flSet_flXform( PAGE_XLATE_CHANGED | DEVICE_TO_WORLD_INVALID); } } return(TRUE); } /******************************Public*Routine******************************\ * BOOL GreExtTextOutW (hdc,x,y,flOpts,prcl,pwsz,cwc,pdx,pvBuffer) * * Write text with lots of random spacing and alignment options. * * Below are the string length distributions from running the Winstone 95 * scenarios for Excel and Winword, courtesy of KirkO: * * [Excel] * ------ OPAQUE ------- ----- TRANSPARENT --- * string length pdx == 0 pdx != 0 pdx == 0 pdx != 0 * ------------- ---------- ---------- ---------- ---------- * 0--9 3799 0 15323 9143 * 10--19 695 0 1651 983 * 20--29 527 0 196 22 * 30--39 200 0 289 53 * 40--49 12 0 3 0 * 50--59 96 0 12 0 * 60--69 10 0 4 0 * 70--79 3 0 0 0 * 80--89 0 0 0 0 * 90--99 0 0 49 16 * 100--109 11 0 0 0 * * [Winword] * * ------ OPAQUE ------- ----- TRANSPARENT --- * string length pdx == 0 pdx != 0 pdx == 0 pdx != 0 * ------------- ---------- ---------- ---------- ---------- * 0--9 1875 9 3350 0 * 10--19 878 17 324 0 * 20--29 254 17 25 0 * 30--39 93 6 28 2 * 40--49 146 30 18 12 * 50--59 34 16 3 14 * 60--69 20 13 8 7 * 70--79 128 73 13 18 * 80--89 53 139 6 23 * 90--99 41 903 9 127 * 100--109 92 1878 3 217 * 110--119 5 23 0 0 * * History: * Fri 05-Nov-1993 -by- Patrick Haluptzok [patrickh] * Make smaller and faster. * * Sat 14-Mar-1992 06:08:26 -by- Charles Whitmer [chuckwh] * Rewrote it. * * Thu 03-Jan-1991 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ #define TS_DRAW_TEXT 0x0001 #define TS_DRAW_OPAQUE_PGM 0x0002 #define TS_DRAW_COMPLEX_UNDERLINES 0x0004 #define TS_DRAW_OPAQUE_RECT 0x0008 #define TS_DRAW_BACKGROUND_PGM 0x0010 #define TS_DRAW_VECTOR_FONT 0x0020 #define TS_DRAW_OUTLINE_FONT 0x0040 #define TS_START_WITH_SUCCESS 0x0080 // Due to the massiveness of this function we will use 2 space tabs. #if DBG // statistics typedef struct _TSTATENTRY { int c; // number observed } TSTATENTRY; typedef struct _TSTAT { TSTATENTRY NO; // pdx == 0, opaque TSTATENTRY DO; // pdx != 0, opaque TSTATENTRY NT; // pdx == 0, transparent TSTATENTRY DT; // pdx != 0, transparent } TSTAT; #define MAX_CHAR_COUNT 200 // observe for 0,1,..200,201 and above typedef struct _TEXTSTATS { int cchMax; // = MAX_CHAR_COUNT TSTAT ats[MAX_CHAR_COUNT+2]; } TEXTSTATS; TEXTSTATS gTS = {MAX_CHAR_COUNT}; #define TS_START 1 #define TS_CONTINUE 2 #define TS_VERBOSE 4 #define TS_STOP 8 FLONG gflTS = 0; // flags that control text statistics #endif // statistics BOOL GreExtTextOutWLocked( XDCOBJ &dco, // Locked DC int x, // Initial x position int y, // Initial y position UINT flOpts, // Options LPRECT prcl, // Clipping rectangle LPWSTR pwsz, // Unicode character array int cwc, // Count of characters LPINT pdx, // Character spacing ULONG ulBkMode, // Current background mode PVOID pvBuffer, // If non-NULL, contains a buffer for storing // the STROBJ, so that we don't need to // allocate one on the fly. Must be at // least SIZEOF_STROBJ_BUFFER(cwc) bytes in // size. // If NULL, we will try to use the global // STROBJ buffer, otherwise we will allocate // on the fly. DWORD dwCodePage // Code page for current textout ) { // flState gets set with BITS for things we need to draw. // We also use this for the return value, setting it to 0 if an error occurs. BOOL bIsVectorFont = FALSE; BOOL bPathFont = FALSE; FLONG flState = TS_START_WITH_SUCCESS; BOOL flType = ((flOpts & ETO_GLYPH_INDEX) ? RFONT_TYPE_HGLYPH : RFONT_TYPE_UNICODE); // Win95 comaptibility: If in path bracket and ETO_CLIPPED is set, then fail call if ( dco.pdc->bActive() && ( flOpts & ETO_CLIPPED ) ) { EngSetLastError( ERROR_INVALID_PARAMETER ); return( FALSE ); } if (dco.bDisplay() && !dco.bRedirection() && !UserScreenAccessCheck()) { SAVE_ERROR_CODE(ERROR_ACCESS_DENIED); return( FALSE ); } // ignore RTOLREADING flag, // only does something if lpk dlls are loaded under win95 BOOL bPdy = flOpts & ETO_PDY; flOpts &= ~(ETO_PDY | ETO_GLYPH_INDEX | ETO_RTLREADING | ETO_IGNORELANGUAGE | ETO_NUMERICSLOCAL | ETO_NUMERICSLATIN); if (!prcl) { flOpts &= ~(ETO_CLIPPED | ETO_OPAQUE); // ignore flags if no rect, win95 compat } else if ((prcl->left == prcl->right) || (prcl->top == prcl->bottom)) { prcl->left = prcl->right = x; prcl->top = prcl->bottom = y; // now prcl is guaranteed to be within text rect if ((flOpts & (ETO_OPAQUE|ETO_CLIPPED)) == ETO_OPAQUE) { prcl = NULL; // since it is empty, we may as well ignore it flOpts &= ~ETO_OPAQUE; // ignore flag if no rect } } PFN_DrvTextOut pfnTextOut; MIX mix = R2_COPYPEN + 256*R2_COPYPEN; // default mix sent to TextOut routines // Lock the DC and set the new attributes. if (dco.bValid()) { #if DBG // statistics if (gflTS & TS_START) { KdPrint(( "\n" "***************************************\n" "*** BEGIN GreExtTextOutW statistics ***\n" "***************************************\n" "\n" )); RtlZeroMemory(gTS.ats, sizeof(TSTAT)*(gTS.cchMax+2)); gflTS ^= TS_START; gflTS |= TS_CONTINUE; } if (gflTS & TS_CONTINUE) { TSTATENTRY *ptse; char *psz; TSTAT *pts; pts = gTS.ats + min(cwc,gTS.cchMax+1); ptse = 0; switch (ulBkMode) { case OPAQUE: ptse = (pdx) ? &(pts->DO) : &(pts->NO); psz = "OPAQUE"; break; case TRANSPARENT: ptse = (pdx) ? &(pts->DT) : &(pts->NT); psz = "TRANSP"; break; default: KdPrint(("jBkMode = UNKNOWN\n")); break; } if (ptse) { ptse->c += 1; if (gflTS & TS_VERBOSE) KdPrint(( "%10d %10d %s %s" , min(cwc,gTS.cchMax+1) , ptse->c , psz , (pdx) ? "(pdx)" : "" )); } } if (gflTS & TS_STOP) { KdPrint(( "\n" "*************************************\n" "*** END GreExtTextOutW statistics ***\n" "*************************************\n" "\n" )); gflTS = 0; } #endif // statistics // Check the validity of the flags. if ( ((flOpts == 0) || ((prcl != (RECT *) NULL) && ((flOpts & ~(ETO_CLIPPED | ETO_OPAQUE)) == 0))) && !(dco.pdc->bActive() && (flOpts & ETO_CLIPPED)) // no clipping in a path ) { FLONG flControl = 0; // Set to 0 in case cwc is 0 ERECTL rclExclude(0,0,0,0); // Lock the Rao region if we are drawing on a display surface. The Rao region // might otherwise change asynchronously. The DEVLOCKOBJ also makes sure that // the VisRgn is up to date, calling the window manager if necessary to // recompute it. This is needed if want to compute positions in screen coords. // No DEVLOCK is needed if we are in path accumulation mode. POINTL ptlOrigin; // The window origin. POINTFIX ptfxOrigin; // Same thing in FIX. if (dco.pdc->bActive()) { ptlOrigin.x = 0; ptlOrigin.y = 0; } else { ptlOrigin = dco.eptlOrigin(); } ptfxOrigin.x = LTOFX(ptlOrigin.x); ptfxOrigin.y = LTOFX(ptlOrigin.y); // Get the transform from the DC. EXFORMOBJ xo(dco, WORLD_TO_DEVICE); // Transform the input rectangle. In the simple case the result is in // rclInput. In the general case a parallelogram is in ptfxInput[4], and // bounds for the parallelogram are in rclInput. ERECTL rclInput; POINTFIX ptfxInput[4]; // this must go after the DCOBJ, so that the DCOBJ still exists at cleanup TXTCLEANUP clean; // Mops up before exit. if (prcl != (RECT *) NULL) { if (flOpts & ETO_OPAQUE) { flState |= TS_DRAW_OPAQUE_RECT; } // The intent of the following bTranslationsOnly and bScale cases is to provide // faster inline versions of the same code that would be executed by xo.bXform(prcl). // We must be completely compatible with the normal xform code because apps expect // to opaque and clip to the same rectangles as a PatBlt would cover. So, if you // intend to modify the behavior of this code, make sure you change PatBlt and // BitBlt as well! (I.e. just don't do it.) [chuckwh] if (xo.bTranslationsOnly()) { rclInput.left = prcl->left + ptlOrigin.x + FXTOL(xo.fxDx() + 8); rclInput.right = prcl->right + ptlOrigin.x + FXTOL(xo.fxDx() + 8); rclInput.top = prcl->top + ptlOrigin.y + FXTOL(xo.fxDy() + 8); rclInput.bottom = prcl->bottom + ptlOrigin.y + FXTOL(xo.fxDy() + 8); } else if (xo.bScale()) // Simple scaling. { rclInput.left = FXTOL(xo.fxFastX(prcl->left) + 8) + ptlOrigin.x; rclInput.right = FXTOL(xo.fxFastX(prcl->right) + 8) + ptlOrigin.x; rclInput.top = FXTOL(xo.fxFastY(prcl->top) + 8) + ptlOrigin.y; rclInput.bottom = FXTOL(xo.fxFastY(prcl->bottom) + 8) + ptlOrigin.y; } else // General case. { // // Construct three vertices of the input rectangle, in drawing order. // ptfxInput[0].x = prcl->left; ptfxInput[0].y = prcl->bottom; ptfxInput[1].x = prcl->left; ptfxInput[1].y = prcl->top; ptfxInput[2].x = prcl->right; ptfxInput[2].y = prcl->top; // Transform the vertices. xo.bXform((POINTL *) ptfxInput,ptfxInput,3); // We construct the fourth vertex ourselves to avoid excess transform // work and roundoff errors. ptfxInput[3].x = ptfxInput[0].x + ptfxInput[2].x - ptfxInput[1].x; ptfxInput[3].y = ptfxInput[0].y + ptfxInput[2].y - ptfxInput[1].y; // Bound the parallelogram. (Using Black Magic.) // DChinn: At this point, we know that EITHER points 0 and 2 OR // points 1 and 3 are the extreme x coordinates (left and right). // Similarly, EITHER points 0 and 2 OR points 1 and 3 are the extreme y // coordinates. // // ASSERT: it is possible that at this point, prcl is inverted // (i.e., left > right or top > bottom). If this is true, then // ptfxInput[] will have its points ordered counterclockwise rather // than clockwise. int ii; ii = (ptfxInput[1].x > ptfxInput[0].x) == (ptfxInput[1].x > ptfxInput[2].x); // Rounding direction depends on whether the parallelogram is inverted or not. if (ptfxInput[ii].x <= ptfxInput[ii+2].x) { rclInput.left = ptlOrigin.x + FXTOL(ptfxInput[ii].x); rclInput.right = ptlOrigin.x + FXTOLCEILING(ptfxInput[ii+2].x); } else { rclInput.left = ptlOrigin.x + FXTOLCEILING(ptfxInput[ii].x); rclInput.right = ptlOrigin.x + FXTOL(ptfxInput[ii+2].x); } ii = (ptfxInput[1].y > ptfxInput[0].y) == (ptfxInput[1].y > ptfxInput[2].y); // Rounding direction depends on whether the parallelogram is inverted or not. if (ptfxInput[ii].y <= ptfxInput[ii+2].y) { rclInput.top = ptlOrigin.y + FXTOL(ptfxInput[ii].y); rclInput.bottom = ptlOrigin.y + FXTOLCEILING(ptfxInput[ii+2].y); } else { rclInput.top = ptlOrigin.y + FXTOLCEILING(ptfxInput[ii].y); rclInput.bottom = ptlOrigin.y + FXTOL(ptfxInput[ii+2].y); } // Take care of a complex clipping request now. We'll set things up // so that the clipping will just happen automatically, and then clear // the clipping flag. This simplifies the rest of the code. if (flOpts & ETO_CLIPPED) { // Allocate a path. We know we're not already in a path bracket // since clipping requests in path brackets were rejected above. // Draw the parallelogram into it. PATHMEMOBJ po; if (po.bValid() && po.bAddPolygon(XFORMNULL,(POINTL *)&ptfxInput[0],4)) { // // Construct a region from the path. // RECTL Bounds, *pBounds; // Only the top and bottom are used for clipping and // they have to be initialized as FIX. Bounds.top = LTOFX(dco.erclClip().top - ptlOrigin.y); Bounds.bottom = LTOFX(dco.erclClip().bottom - ptlOrigin.y); pBounds = &Bounds; RGNMEMOBJ rmo(po, ALTERNATE, pBounds); if (rmo.bValid()) { // Stuff the region handle into the DC. This is a nifty trick // (courtesy of DonaldS) that uses this region in the next // calculation of the clipping pipeline. We'll clear the prgnAPI // after we're through. dco.pdc->prgnAPI(rmo.prgnGet()); // Clipping is now transparent, so forget about it. clean.vSet(dco); // This frees the region on exit. if (dco.pdc->prgnRao() != (REGION *)NULL) { dco.pdc->vReleaseRao(); } if (dco.pdc->bCompute()) { flOpts &= ~ETO_CLIPPED; } // Now that we have a complex clip area, we can opaque with a // simple BitBlt. So we don't need to set TS_DRAW_OPAQUE_PGM. } } // // Well if we have succeeded we will have erased the ETO_CLIPPED flag. // Otherwise we failed and need to return FALSE. We can't return here // without generating tons of destructors so we set flState and a few // other fields to 0 so we bop down and return FALSE. // if (flOpts & ETO_CLIPPED) { flOpts = 0; flState = 0; cwc = 0; } } else if (flOpts & ETO_OPAQUE) { flState &= ~TS_DRAW_OPAQUE_RECT; flState |= TS_DRAW_OPAQUE_PGM; // Since we're actually going to use it, offset the parallelogram // to screen coordinates. use unrolled loop EPOINTFIX *pptfx = (EPOINTFIX *) &ptfxInput[0]; *pptfx++ += ptfxOrigin; *pptfx++ += ptfxOrigin; *pptfx++ += ptfxOrigin; *pptfx += ptfxOrigin; } } // If it a mirrored DC then shift the rect one pixel to the right // This will give the effect of including the right edge of the rect and exclude the left edge. if (MIRRORED_DC(dco.pdc)) { ++rclInput.left; ++rclInput.right; } // Force the rectangle to be well ordered. rclInput.vOrder(); // Add any opaquing into the exclusion area. if (flState & (TS_DRAW_OPAQUE_RECT | TS_DRAW_OPAQUE_PGM)) { rclExclude += rclInput; } } POINTFIX aptfxBackground[4]; // The TextBox. BOOL bComplexBackground; // TRUE if the TextBox is a parallelogram. RECTL *prclBackground = NULL; // Bkgnd rectangle passed to DrvTextOut. RECTL *prclExtraRects = NULL; // Extra rectangles passed to DrvTextOut. RFONTOBJ rfo; ESTROBJ to; if (cwc) { // // Locate the font cache. Demand PATHOBJ's if we are in a path bracket. // rfo.vInit(dco, dco.pdc->bActive() ? TRUE : FALSE, flType); if (rfo.bValid()) { bPathFont = rfo.bPathFont(); bIsVectorFont = rfo.bPathFont() && !rfo.bReturnsOutlines(); // The recording of the simulation flags and escapement must come AFTER // the rfont constructor since they're cached values that are copied from // the LFONT only when the font is realized. flControl = (dco.pdc->flTextAlign() & TA_MASK) | dco.pdc->flSimulationFlags(); // Get the reference point. EPOINTFIX ptfx; if (flControl & TA_UPDATECP) { if (dco.pdc->bValidPtfxCurrent()) { // Mark that we'll be updating the DC's CP in device coords only: dco.pdc->vInvalidatePtlCurrent(); ptfx.x = dco.ptfxCurrent().x + ptfxOrigin.x; ptfx.y = dco.ptfxCurrent().y + ptfxOrigin.y; } else { // DC's CP is in logical coords; we have to transform it to device // space and mark that we'll be updating the CP in device space: dco.pdc->vValidatePtfxCurrent(); dco.pdc->vInvalidatePtlCurrent(); if (xo.bTranslationsOnly()) { ptfx.x = LTOFX(dco.ptlCurrent().x) + xo.fxDx(); ptfx.y = LTOFX(dco.ptlCurrent().y) + xo.fxDy(); } else if (xo.bScale()) { ptfx.x = xo.fxFastX(dco.ptlCurrent().x); ptfx.y = xo.fxFastY(dco.ptlCurrent().y); } else { xo.bXform((POINTL *) &dco.ptlCurrent(), &ptfx, 1); } dco.ptfxCurrent() = ptfx; ptfx += ptfxOrigin; } } else { // // The reference point is passed in. Transform it to device coords. // if (xo.bTranslationsOnly()) { ptfx.x = LTOFX(x) + xo.fxDx() + ptfxOrigin.x; ptfx.y = LTOFX(y) + xo.fxDy() + ptfxOrigin.y; } else if (xo.bScale()) { ptfx.x = xo.fxFastX(x) + ptfxOrigin.x; ptfx.y = xo.fxFastY(y) + ptfxOrigin.y; } else { ptfx.x = x; ptfx.y = y; xo.bXform((POINTL *) &ptfx,&ptfx,1); ptfx += ptfxOrigin; } } // The STROBJ will now compute the text alignment, character positions, // and TextBox. to.vInit ( pwsz, cwc, dco, rfo, xo, (LONG *) pdx, bPdy, dco.pdc->lEscapement(), // Only read this after RFONTOBJ! dco.pdc->lTextExtra(), dco.pdc->lBreakExtra(), dco.pdc->cBreak(), ptfx.x,ptfx.y, flControl, (LONG *) NULL, pvBuffer, dwCodePage ); if (to.bValid()) { // Compute the bounding box and the background opaquing area. The // parallelogram aptfxBackground is only computed when it's complex. bComplexBackground = to.bOpaqueArea(aptfxBackground, &to.rclBkGround); // This hack, due to filtering needs to be put in textgdi.cxx // filtering leeks color one pixel to the left and one pixel to the right // The trouble with it is that is it going to add a pixel on each side // to the text background when text is painted in the OPAQUE mode. if (rfo.pfo()->flFontType & FO_CLEARTYPE_X) { to.rclBkGround.left--; to.rclBkGround.right++; } if (to.bLinkedGlyphs()) { to.vEudcOpaqueArea(aptfxBackground, bComplexBackground); } // Accumulate the touched area to the exclusion rect. rclExclude += to.rclBkGround; // Make notes of exactly what drawing needs to be done. if (ulBkMode == OPAQUE) { if (bComplexBackground) { flState |= TS_DRAW_BACKGROUND_PGM; } else { prclBackground = &to.rclBkGround; // No TS_ bit since this gets } // passed directly to DrvTextOut. } // In a few bizarre cases the STROBJ can have an empty text rectangle // we don't want to call DrvTextOut in those case but still need // to worry about the opaque rectangle. BOOL bEmptyTextRectangle = ((ERECTL *)&(to.rclBkGround))->bEmpty(); // Attempt to combine the rectangles. Even in transparent mode we should // attempt to send an opaquing rectangle to DrvTextOut in prclBackground. if ((flState & TS_DRAW_OPAQUE_RECT) && (rclInput.bContain(to.rclBkGround)) && (!bEmptyTextRectangle) ) { prclBackground = &rclInput; flState &= ~TS_DRAW_OPAQUE_RECT; } if ( ((prclBackground != NULL ) && !((ERECTL *)prclBackground)->bEmpty() ) || ((prclBackground == NULL) && !bEmptyTextRectangle) ) { flState |= TS_DRAW_TEXT; } if (flControl & (TSIM_UNDERLINE1 | TSIM_STRIKEOUT)) { if ((prclExtraRects = to.prclExtraRects()) == NULL) { flState |= TS_DRAW_COMPLEX_UNDERLINES; } else // include extra rectangles into the touched area: { ERECTL *eprcl = (ERECTL *) prclExtraRects; for (; !eprcl->bEmpty(); eprcl++) { rclExclude += *eprcl; } } } // Accelerate the clipping case when it's irrelevant. (I'm concerned // that a spreadsheet might tell us to clip to a cell, even though the // string lies completely within.) if (flOpts & ETO_CLIPPED) { if (rclInput.bContain(rclExclude)) { flOpts &= ~ETO_CLIPPED; } else { rclExclude *= rclInput; } } } else // if (to.bValid()) { flState = 0; } } else // if (rfo.bValid()) { flState = 0; } } // if (cwc) if (flControl & TA_UPDATECP) { dco.ptfxCurrent().x += to.ptfxAdvance().x; dco.ptfxCurrent().y += to.ptfxAdvance().y; } // // Draw the text into a path if we're in a path bracket. // if (dco.pdc->bActive()) { // // We fail this call if we are asked to CLIP while in a path bracket. // if (flOpts & ETO_CLIPPED) { flState = 0; } XEPATHOBJ po(dco); if (po.bValid()) { // Draw the various background shapes. if (flState & TS_DRAW_OPAQUE_RECT) { if (!bAddRectToPath(po,&rclInput)) { flState = 0; } } if (flState & TS_DRAW_OPAQUE_PGM) { if (!po.bAddPolygon(XFORMNULL,(POINTL *) &ptfxInput[0],4)) { flState = 0; } } if (flState & TS_DRAW_BACKGROUND_PGM) { if (!po.bAddPolygon(XFORMNULL,(POINTL *) aptfxBackground,4)) { flState = 0; } } BOOL bNeedUnflattend = FALSE; // // Draw the background rect, text, and extra rectangles. // if (flState & TS_DRAW_TEXT) { // // Draw a background rectangle. // if ((prclBackground == (RECTL *) NULL) || bAddRectToPath(po,prclBackground)) { // // Draw the text. // bNeedUnflattend = ( dco.flGraphicsCaps() & GCAPS_BEZIERS ) ? FALSE : TRUE; if (!to.bTextToPath(po, dco, bNeedUnflattend)) { flState = 0; } else { // // Draw extra rectangles. // if (prclExtraRects != (ERECTL *) NULL) { ERECTL *eprcl = (ERECTL *) prclExtraRects; for (; !eprcl->bEmpty(); eprcl++) { if (!bAddRectToPath(po,eprcl)) { flState = 0; break; } } } } } // if ((prclBackground==0)||bAddRectToPath(po,prclBackground)) } // if (flState & TS_DRAW_TEXT) // // Handle complex cases of strikeout and underlines. // if (flState & TS_DRAW_COMPLEX_UNDERLINES) { if (!to.bExtraRectsToPath(po, bNeedUnflattend)) { flState = 0; } } } // if (po.bValid()) } else // if (dco.pdc->bActive()) { // // If there's nothing to do, get out now. // if (!rclExclude.bEmpty()) { // Accumulate bounds. We can do this before knowing if the operation is // successful because bounds can be loose. if (dco.fjAccum()) { // Use a temporary exclusion rect in device coordinates. ERECTL rclExcludeDev; rclExcludeDev.left = rclExclude.left - ptlOrigin.x; rclExcludeDev.right = rclExclude.right - ptlOrigin.x; rclExcludeDev.top = rclExclude.top - ptlOrigin.y; rclExcludeDev.bottom = rclExclude.bottom - ptlOrigin.y; dco.vAccumulate(*((ERECTL *) &rclExcludeDev)); } // Compute the clipping complexity and maybe reduce the exclusion rectangle. // It appears that a 10pt script font (on a screen DC) can be outside of // the bounds of the background rectangle. We have a situation where // an app creates a memory DC exactly the size of the bouding rectangle // and then draws a vector font to it. We compute the cliping to be // trivial since we think the vector string fits completely inside // the bounds of the DC and blow up later in the code. Rather than tweak // the vector font driver at this point in the game I am going to force // cliping to be set for vector fonts. Later on we should figure out // why this is happening and fix the vector font driver. [gerritv] // Under some xform, the text might be "stretched" to some extent length // and it will go to the bSimpleFill path code. Force clipping for this // case, otherwise we might hit the AV since recl's generated from the // pathobj might be a pixel off than the clip rclBounds . ECLIPOBJ co(dco.prgnEffRao(),rclExclude, bPathFont || (flOpts & ETO_CLIPPED) ? CLIP_FORCE : CLIP_NOFORCE); rclExclude = co.erclExclude(); // Check the destination which is reduced by clipping. if (!co.erclExclude().bEmpty()) { SURFACE *pSurf = dco.pSurface(); if (pSurf != NULL) { PDEVOBJ pdo(pSurf->hdev()); XEPALOBJ palDest(pSurf->ppal()); XEPALOBJ palDestDC(dco.ppal()); // Get the foreground and opaque brushes FLONG flCaps = dco.flGraphicsCaps(); EBRUSHOBJ *peboText = dco.peboText(); EBRUSHOBJ *peboBackground = dco.peboBackground(); BOOL bDitherText = FALSE; if ( flCaps & GCAPS_ARBRUSHTEXT ) { // Even if a printer driver sets GCAPS_ARBRUSHTEXT, we // can't allow vector fonts to be dithered. bDitherText = !bIsVectorFont; // We always dirty the text brush if ARBRUSHTEXT is set to // catch the cases where we transition between vector and // non-vector fonts but keep the same brush -- if we didn't // do this, we might try to use a cached, dithered brush // after switching from a TrueType font to a vector font. dco.ulDirtyAdd(DIRTY_TEXT); // Get through the are-you-really-dirty check in vInitBrush: peboText->vInvalidateUniqueness(); } if ( dco.bDirtyBrush(DIRTY_TEXT|DIRTY_BACKGROUND) ) { if ( dco.bDirtyBrush(DIRTY_TEXT) ) { peboText->vInitBrush(dco.pdc, gpbrText, palDestDC, palDest, pSurf, bDitherText); } if ( dco.bDirtyBrush(DIRTY_BACKGROUND) ) { peboBackground->vInitBrush(dco.pdc, gpbrBackground, palDestDC, palDest, pSurf, (flCaps & GCAPS_ARBRUSHOPAQUE) ? TRUE : FALSE); } dco.vCleanBrush(DIRTY_TEXT|DIRTY_BACKGROUND); } // exclude the pointer DEVEXCLUDEOBJ dxo(dco,&co.erclExclude(),&co); // // Draw background shapes that are too complex for DrvTextOut. // POINTL *pptlBO = &dco.pdc->ptlFillOrigin(); // There's an extra layer of conditional here so that simple text can just // jump over it all. if (flState & (TS_DRAW_OPAQUE_RECT | TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM)) { if ((flState & TS_DRAW_OPAQUE_RECT) && !rclInput.bEmpty()) { // intersect the dest rect with the clip rect and set it in co // we can only get away with touching co.rclBounds after // the ECLIPOBJ constructor because of two reasons: // a) the target rectangle passed to bitblt is contained in the // original bounds set by ECLIPOBJ, being the intersection // of the origianal bounds with THE intended target rectangle. // b) clipping complexity may have changed when we changed // co.erclExclude, but it only could have gotten simpler, // so at worst in those rare situations we would not go // through the optimal code path. // By changing clipping bounds we accomplish that no intersection // of the target rectangle with clipping region rectangle is emtpy // relieving the driver of extra work [bodind] co.erclExclude().left = max(rclExclude.left,rclInput.left); co.erclExclude().right = min(rclExclude.right,rclInput.right); co.erclExclude().top = max(rclExclude.top,rclInput.top); co.erclExclude().bottom = min(rclExclude.bottom,rclInput.bottom); // if not clipped, Just paint the rectangle. if ((co.erclExclude().left < co.erclExclude().right) && (co.erclExclude().top < co.erclExclude().bottom)) { // Inc target surface uniqueness INC_SURF_UNIQ(pSurf); TextOutBitBlt(pSurf, rfo, (SURFOBJ *) NULL, (SURFOBJ *) NULL, &co, NULL, &co.rclBounds, (POINTL *) NULL, (POINTL *) NULL, (BRUSHOBJ *)peboBackground, pptlBO, 0x0000f0f0); } co.erclExclude() = rclExclude; } if (flState & (TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM)) { PATHMEMOBJ po; if (po.bValid()) { if (flState & TS_DRAW_OPAQUE_PGM) { if (!po.bAddPolygon(XFORMNULL,(POINTL *) &ptfxInput[0],4)) { flState = 0; } } if (flState & TS_DRAW_BACKGROUND_PGM) { if (!po.bAddPolygon(XFORMNULL,(POINTL *) aptfxBackground,4)) { flState = 0; } } if (flState & (TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM)) { if (!po.bTextOutSimpleFill(dco, rfo, &pdo, pSurf, &co, (BRUSHOBJ *)peboBackground, pptlBO, (R2_COPYPEN << 8) | R2_COPYPEN, WINDING)) { flState = 0; } } // if (flState & (TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM)) } // if (po.bValid()) } // if (flState & (TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM)) } // if (flState & (TS_DRAW_OPAQUE_RECT | TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM)) // // Draw the background rect, text, and extra rectangles. // if (flState & TS_DRAW_TEXT) { ERECTL *prclSimExtra = NULL; // Prepare for a vector font simulation. Note that we'll also need to // simulate the background and extra rectangles. if (bPathFont) { flCaps &= ~(GCAPS_OPAQUERECT); flState |= (rfo.bReturnsOutlines()) ? TS_DRAW_OUTLINE_FONT:TS_DRAW_VECTOR_FONT; } // Simulate a background rectangle. if ((prclBackground != (RECTL *) NULL) && !(flCaps & GCAPS_OPAQUERECT)) { // intersect the dest rect with the clip rect and set it in co // we can only get away with touching co.rclBounds after // the ECLIPOBJ constructor because of two reasons: // a) the target rectangle passed to bitblt is contained in the // original bounds set by ECLIPOBJ, being the intersection // of the origianal bounds with THE intended target rectangle. // b) clipping complexity may have changed when we changed // co.erclExclude, but it only could have gotten simpler, // so at worst in those rare situations we would not go // through the optimal code path. // By changing clipping bounds we accomplish that no intersection // of the target rectangle with clipping region rectangle is emtpy // relieving the driver of extra work [bodind] co.erclExclude().left = max(rclExclude.left,prclBackground->left); co.erclExclude().right = min(rclExclude.right,prclBackground->right); co.erclExclude().top = max(rclExclude.top,prclBackground->top); co.erclExclude().bottom = min(rclExclude.bottom,prclBackground->bottom); // if not clipped, Just paint the rectangle. if ((co.erclExclude().left < co.erclExclude().right) && (co.erclExclude().top < co.erclExclude().bottom)) { // Inc target surface uniqueness INC_SURF_UNIQ(pSurf); TextOutBitBlt(pSurf, rfo, (SURFOBJ *) NULL, (SURFOBJ *) NULL, &co, NULL, &co.rclBounds, (POINTL *) NULL, (POINTL *) NULL, (BRUSHOBJ *)peboBackground, pptlBO, 0x0000f0f0); } co.erclExclude() = rclExclude; prclBackground = NULL; } // Prepare for the extra rectangle simulation. // For TTY drivers, we need to pass prclExtraRects // to the DrvTextOut call. if ((prclExtraRects != (ERECTL *) NULL) && (pdo.ulTechnology() != DT_CHARSTREAM)) { prclSimExtra = (ERECTL *) prclExtraRects; prclExtraRects = NULL; } // Draw the text. if (flState & TS_DRAW_VECTOR_FONT) { #ifdef FE_SB if( to.bLinkedGlyphs() ) { if( ! bProxyDrvTextOut ( dco, pSurf, to, co, (RECTL*) NULL, (RECTL*) NULL, (BRUSHOBJ*) peboText, (BRUSHOBJ*) peboBackground, pptlBO, rfo, &pdo, dco.flGraphicsCaps(), &rclExclude )) { flState = 0; } } else { #endif PATHMEMOBJ po; if ((!po.bValid()) || (!to.bTextToPath(po, dco)) || (!po.bTextOutSimpleStroke1(dco, rfo, &pdo, pSurf, &co, (BRUSHOBJ *)peboText, pptlBO, (R2_COPYPEN | (R2_COPYPEN << 8))))) { flState = 0; } } } else // if (flState & TS_DRAW_VECTOR_FONT) { if (pSurf->pdcoAA) { RIP("pdcoAA != 0\n"); pSurf->pdcoAA = 0; // let the free build run } pfnTextOut = pSurf->pfnTextOut(); // If the pointer to the TextOut function points to SpTextOut then // we know that AntiAliased text can be handled and we can skip // the funny business in the else clause if (pfnTextOut == SpTextOut) { if (rfo.prfnt->fobj.flFontType & (FO_GRAY16 | FO_CLEARTYPE_X)) { pSurf->pdcoAA = &dco; } } else { if ( (rfo.prfnt->fobj.flFontType & FO_GRAY16) && (!(dco.flGraphicsCaps() & GCAPS_GRAY16) || (rfo.prfnt->fobj.flFontType & FO_CLEARTYPE_X)) ) { // Inform SpTextOut that this call came from GreExtTextOutW // for the purpose of rendering anti aliased text on a device // that does not support it. Remember to set this to zero // before releasing the surface to other users. if (pfnTextOut != EngTextOut) pSurf->pdcoAA = &dco; pfnTextOut = SpTextOut; } } // if (pfnTextOut == SpTextOut) else if (flState & TS_DRAW_OUTLINE_FONT) { #ifdef FE_SB if( to.bLinkedGlyphs() ) { if( ! bProxyDrvTextOut ( dco, pSurf, to, co, (RECTL*) NULL, (RECTL*) NULL, (BRUSHOBJ*) peboText, (BRUSHOBJ*) peboBackground, pptlBO, rfo, &pdo, dco.flGraphicsCaps(), &rclExclude )) { flState = 0; } } else { #endif PATHMEMOBJ po; if ((!po.bValid()) || (!to.bTextToPath(po, dco)) || ( ( po.cCurves > 1 ) && !po.bTextOutSimpleFill(dco, rfo, &pdo, pSurf, &co, (BRUSHOBJ *)peboText, pptlBO, (R2_COPYPEN | (R2_COPYPEN << 8)), WINDING))) { flState = 0; } } } else // if (flState & TS_DRAW_OUTLINE_FONT) { if (flState & TS_DRAW_COMPLEX_UNDERLINES) { INC_SURF_UNIQ(pSurf); // Unfortunately, many drivers destroy the coordinates in our // GLYPHPOS array. This means that any complex underlining has // to be calculated now. PATHMEMOBJ po; if ((!po.bValid()) || (!to.bExtraRectsToPath(po)) || #ifdef FE_SB ((to.bLinkedGlyphs() ) ? (!bProxyDrvTextOut ( dco, pSurf, to, co, prclExtraRects, prclBackground, peboText, peboBackground, pptlBO, rfo, (PDEVOBJ *) NULL, (FLONG) 0L, &rclExclude )) : #endif (!((*pfnTextOut) (pSurf->pSurfobj(), (STROBJ *) &to, rfo.pfo(), &co, prclExtraRects, prclBackground, (BRUSHOBJ *)peboText, (BRUSHOBJ *)peboBackground, pptlBO, mix))) ) || (!po.bTextOutSimpleFill(dco, rfo, &pdo, pSurf, &co, (BRUSHOBJ *)peboText, pptlBO, (R2_COPYPEN | (R2_COPYPEN << 8)), WINDING))) { flState = 0; } flState &= ~TS_DRAW_COMPLEX_UNDERLINES; } // if (flState & TS_DRAW_COMPLEX_UNDERLINES) else { // Inc target surface uniqueness INC_SURF_UNIQ(pSurf); #ifdef FE_SB if( to.bLinkedGlyphs() ) { if( !bProxyDrvTextOut ( dco, pSurf, to, co, prclExtraRects, prclBackground, peboText, peboBackground, pptlBO, rfo, &pdo, (FLONG) 0, &rclExclude )) { flState = 0; } } else { #endif rfo.PreTextOut(dco); if (!(*pfnTextOut)(pSurf->pSurfobj(), (STROBJ *) &to, rfo.pfo(), &co, prclExtraRects, prclBackground, (BRUSHOBJ *)peboText, (BRUSHOBJ *)peboBackground, pptlBO, mix)) { flState = 0; } rfo.PostTextOut(dco); } } // if (flState & TS_DRAW_COMPLEX_UNDERLINES) else } // if (flState & TS_DRAW_OUTLINE_FONT) else ASSERTGDI(pSurf != NULL, "pSurf null after EngTextOut"); /* we have seen pSurf being null with privates binaries */ if (pSurf != NULL) pSurf->pdcoAA = 0; // clear AA state; } // if (flState & TS_DRAW_VECTOR_FONT) else // Simulate extra rectangles. if (prclSimExtra != (ERECTL *) NULL) { ERECTL erclOrg; erclOrg = co.erclExclude(); // Inc target surface uniqueness INC_SURF_UNIQ(pSurf); for (; !prclSimExtra->bEmpty(); prclSimExtra++) { // intersect the dest rect with the clip rect and set it in co co.erclExclude().left = max(erclOrg.left,prclSimExtra->left); co.erclExclude().right = min(erclOrg.right,prclSimExtra->right); if (co.erclExclude().left >= co.erclExclude().right) continue; co.erclExclude().top = max(erclOrg.top,prclSimExtra->top); co.erclExclude().bottom = min(erclOrg.bottom,prclSimExtra->bottom); if (co.erclExclude().top >= co.erclExclude().bottom) { continue; } // // not completely clipped, do the bitblt // TextOutBitBlt(pSurf, rfo, (SURFOBJ *) NULL, (SURFOBJ *) NULL, &co, NULL, &co.rclBounds, (POINTL *) NULL, (POINTL *) NULL, (BRUSHOBJ *)peboText, pptlBO, 0x0000f0f0); } co.erclExclude() = erclOrg; } // if (prclSimExtra != (ERECTL *) NULL) } // if (flState & TS_DRAW_TEXT) // // Handle complex cases of strikeout and underlines. // if (flState & TS_DRAW_COMPLEX_UNDERLINES) { PATHMEMOBJ po; if (po.bValid()) { if (!to.bExtraRectsToPath(po) || !po.bTextOutSimpleFill(dco, rfo, &pdo, pSurf, &co, (BRUSHOBJ *)peboText, pptlBO, (R2_COPYPEN | (R2_COPYPEN << 8)), WINDING)) { flState = 0; } } else // if (po.bValid()) { flState = 0; } } // if (flState & TS_DRAW_COMPLEX_UNDERLINES) } // if (pSurf != NULL) } // if (!co.erclExclude().bEmpty()) } // if (!rclExclude.bEmpty()) } // else if (dco.pdc->bActive()) } else // if (((flOpts == 0) || // ((prcl != (RECT *) NULL) && // ((flOpts & ~(ETO_CLIPPED | ETO_OPAQUE)) == 0))) // && !(dco.pdc->bActive() && (flOpts & ETO_CLIPPED))) { WARNING1("Invalid flags for ExtTextOut.\n"); flState = 0; } } else { flState = 0; } // // flState is filled with all the stuff we need to do. // If it is 0 we failed. // // return(flState /* != 0 */); return(flState != 0); } /******************************Public*Routine******************************\ * BOOL GreGetTextExtentW * * Computes the size of the text box in logical coordinates. The text box * has sides which are parallel to the baseline of the text and its ascent * direction. The height is measured in the ascent direction. The width * is measured in the baseline direction. This definition does not change * for transformed text. * * History: * Sat 14-Mar-1992 05:39:04 -by- Charles Whitmer [chuckwh] * Wrote it. \**************************************************************************/ #define PDXNULL (LONG *) NULL BOOL GreGetTextExtentW( HDC hdc, // device context LPWSTR pwsz, // pointer to a UNICODE text string int cwc, // Count of chars. PSIZE pSize, // address to return the dimensions of the string to UINT fl // internal flags ) { BOOL bRet = FALSE; if (cwc == 0) { // // Nothing to do, but we do not fail. // pSize->cx = 0; pSize->cy = 0; bRet = TRUE; } else { // // Lock the DC and set the new attributes. // DCOBJ dco(hdc); if (dco.bValid()) { RFONTOBJ rfo(dco,FALSE, (fl & GGTE_GLYPH_INDEX) ? RFONT_TYPE_HGLYPH : RFONT_TYPE_UNICODE); if (rfo.bValid()) { // fix up glyph indices for bitmap fonts if (rfo.prfnt->flType & RFONT_TYPE_HGLYPH) rfo.vFixUpGlyphIndices((USHORT *)pwsz, cwc); // If we have (escapement != orientation) we return the extents of the // bounding parallelogram. This is not Windows compatible, but then // Windows can't draw it either. LONG lEsc = dco.pdc->lEscapement(); // Only read this after RFONTOBJ! if ((lEsc != (LONG)rfo.ulOrientation()) && ((rfo.iGraphicsMode() != GM_COMPATIBLE) || (rfo.prfnt->flInfo & FM_INFO_TECH_STROKE))) { // // Get the transform from the DC. // EXFORMOBJ xo(dco,WORLD_TO_DEVICE); // // The STROBJ will now compute the text alignment, character positions, // and TextBox. // ESTROBJ to(pwsz, cwc, dco, rfo, xo, PDXNULL,FALSE, lEsc, dco.pdc->lTextExtra(), dco.pdc->lBreakExtra(), dco.pdc->cBreak(), 0, 0, 0, PDXNULL); if (to.bValid()) { // // Transform the TextBox to logical coordinates. // bRet = to.bTextExtent(rfo,lEsc,pSize); } } else { // // At this point we have (escapement == orientation) so we can just run // some pretty trivial code. // bRet = rfo.bTextExtent(dco, pwsz, cwc, lEsc, dco.pdc->lTextExtra(), dco.pdc->lBreakExtra(), dco.pdc->cBreak(), fl, pSize); // // finally if this is compatible mode and a vector font, do win31 // crazyness about text extent: "rotate" cx and cy by esc vector. // This is totally crazy, and is different from what win31 is doing // for tt, but it turns out that quatro pro for windows has figured // this out and that they use this "feature" [bodind] // if (bRet && lEsc && (dco.pdc->iGraphicsMode() == GM_COMPATIBLE) && !dco.pdc->bUseMetaPtoD() && (rfo.prfnt->flInfo & FM_INFO_TECH_STROKE)) { EVECTORFL evfl((LONG)pSize->cx, (LONG)pSize->cy); EFLOATEXT efAngle = lEsc; efAngle /= (LONG)10; MATRIX mx; mx.efM11 = efCos(efAngle); mx.efM11.vAbs(); mx.efM22 = mx.efM11; mx.efM12 = efSin(efAngle); mx.efM12.vAbs(); mx.efM21 = mx.efM12; mx.efDx.vSetToZero(); mx.efDy.vSetToZero(); EXFORMOBJ xoExt(&mx,COMPUTE_FLAGS | XFORM_FORMAT_LTOL); if ((bRet = xoExt.bXform(evfl)) != FALSE) { evfl.x.vAbs(); evfl.y.vAbs(); bRet = evfl.bToPOINTL(*(POINTL *)pSize); } } } } else { WARNING("gdisrv!GreGetTextExtentW(): could not lock HRFONT\n"); } } } return(bRet); } /******************************Public*Routine******************************\ * RFONTOBJ::bTextExtent (pwsz,lExtra,lBreakExtra,cBreak,cc,fl,psizl) * * * * A quick function to compute text extents on the server side. Only * * handles the case where (escapement==orientation). Call the ESTROBJ * * version for the other very hard case. * * * * Thu 14-Jan-1993 04:00:57 -by- Charles Whitmer [chuckwh] * * Wrote it. OK, so it's a blatant ripoff of my bComputeTextExtent from * * the client side. Fine. * \**************************************************************************/ #define CTE_BATCH 82 BOOL RFONTOBJ::bTextExtent( XDCOBJ &dco, LPWSTR pwsz, int cc, LONG lEsc, LONG lExtra, LONG lBreakExtra, LONG cBreak, UINT fl, SIZE *psizl ) { LONG fxBasicExtent; int ii, cNoBackup; FIX fxCharExtra = 0; FIX fxBreakExtra; FIX fxExtra = 0; GLYPHPOS agpos[CTE_BATCH]; // Default set of GLYPHPOS structures. // Compute the basic extent. Batch the glyphs through our array. if (lExtra) { fxCharExtra = lCvt(efWtoDBase(),lExtra); cNoBackup = 0; } #ifndef FE_SB if(lCharInc() == 0) #endif { fxBasicExtent = 0; // NOTE PERF: This is the loop that PaulB would like to optimize with a // special cache access function. Why create the GLYPHPOS // array? [chuckwh] int cBatch; int cLeft = cc; WCHAR *pwc = pwsz; while (cLeft) { cBatch = cLeft; if (cBatch > CTE_BATCH) cBatch = CTE_BATCH; // Get the glyph data. if (!bGetGlyphMetrics(cBatch,agpos,pwc,&dco)) return(FALSE); // Sum the advance widths. for (ii=0; iipgd()->fxD; // the layout code won't allow lExtra to backup a character behind // its origin so keep track of the number of times this happens if( ( fxCharExtra < 0 ) && ( ((EGLYPHPOS *) &agpos[ii])->pgd()->fxD + fxCharExtra <= 0 ) ) { cNoBackup += 1; } } cLeft -= cBatch; pwc += cBatch; } } // Adjust for CharExtra. if (lExtra) { PDEVOBJ pdo(prfnt->hdevConsumer); ASSERTGDI(pdo.bValid(), "bTextExtentRFONTOBJ(): PDEVOBJ constructor failed\n"); if ( (fl & GGTE_WIN3_EXTENT) && pdo.bDisplayPDEV() && (!(prfnt->flInfo & FM_INFO_TECH_STROKE)) ) fxExtra = fxCharExtra * ((lExtra > 0) ? cc : (cc - 1)); else fxExtra = fxCharExtra * ( cc - cNoBackup ); } // Adjust for lBreakExtra. if (lBreakExtra && cBreak) { // Track down the break character. PFEOBJ pfeo(ppfe()); IFIOBJ ifio(pfeo.pifi()); // Compute the extra space in device units. fxBreakExtra = lCvt(efWtoDBase(),lBreakExtra) / cBreak; // Windows won't let us back up over a break. Set up the BreakExtra // to just cancel out what we've already got. if (fxBreakExtra + fxBreak() + fxCharExtra < 0) fxBreakExtra = -(fxBreak() + fxCharExtra); // Add it up for all breaks. WCHAR wcBreak = (fl & GGTE_GLYPH_INDEX)? (WCHAR)hgBreak():ifio.wcBreakChar(); WCHAR *pwc = pwsz; for (ii=0; iicx = (fxBasicExtent + 8) >> 4; else psizl->cx = lCvt(efDtoWBase_31(),fxBasicExtent); if (efDtoWAscent_31().bIs1Over16()) psizl->cy = lMaxHeight(); else psizl->cy = lCvt(efDtoWAscent_31(),lMaxHeight() << 4); #ifdef FE_SB if( gbDBCSCodePage && // Only in DBCS system locale (iGraphicsMode() == GM_COMPATIBLE) && // We are in COMPAPIBLE mode !(flInfo() & FM_INFO_ARB_XFORMS) && // The driver couldnt do arbitrary rotations !(flInfo() & FM_INFO_TECH_STROKE) && // The driver is not vector driver (flInfo() & FM_INFO_90DEGREE_ROTATIONS) && // Driver does 90 degree rotations (lEsc == 900L || lEsc == 2700L) // Current font Escapemant is 900 or 2700 ) { LONG lSwap = psizl->cx; psizl->cx = psizl->cy; psizl->cy = lSwap; } #endif return(TRUE); } /******************************Public*Routine******************************\ * GreSetTextJustification (hdc,lBreakExtra,cBreak) * * * * Sets the amount of extra spacing we'd like to add for each break (space) * * character to (lBreakExtra/cBreak) in logical coordinates. * * * * History: * * Fri 13-Mar-1992 02:25:12 -by- Charles Whitmer [chuckwh] * * Wrote it. * \**************************************************************************/ BOOL APIENTRY NtGdiSetTextJustification( HDC hdc, int lBreakExtra, // Space in logical units to be added to the line. int cBreak // Number of break chars in the line. ) { BOOL bRet; DCOBJ dco(hdc); if ((bRet = dco.bValid()) != FALSE) { dco.pdc->lBreakExtra(lBreakExtra); dco.pdc->cBreak(cBreak); } return(bRet); } /******************************Public*Routine******************************\ * BOOL GreGetTextExtentExW * * * * Determines the number of characters in the input string that fit into * * the given max width (with the widths computed along the escapement * * vector). The partial widths (the distance from the string origin to * * a given character with the width of that character included) for each of * * character. * * * * Returns: * * TRUE if successful, FALSE otherwise. * * * * History: * * Sat 14-Mar-1992 06:03:32 -by- Charles Whitmer [chuckwh] * * Rewrote with new ESTROBJ technology. * * * * 06-Jan-1992 -by- Gilman Wong [gilmanw] * * Wrote it. * \**************************************************************************/ BOOL GreGetTextExtentExW( HDC hdc, // device context LPWSTR pwsz, // pointer to a UNICODE text string COUNT cwc, // count of WCHARs in the string ULONG dxMax, // maximum width to return COUNT *pcChars, // number of chars that fit in dxMax PULONG pdxOut, // offset of each character from string origin LPSIZE pSize, // return height and width of string FLONG fl ) { BOOL bRet = FALSE; PVOID pv; #ifdef DUMPCALL DbgPrint("\nGreGetTextExtentExW(" ); DbgPrint("\n HDC hdc = %-#8lx\n", hdc ); DbgPrint("\n LPWSTR pwsz = %-#8lx -> \"%ws\"\n", pwsz ,pwsz ); DbgPrint("\n COUNT cwc = %d\n", cwc ); DbgPrint("\n ULONG dxMax = %-#8lx\n", dxMax ); DbgPrint("\n COUNT *pcChars = %-#8lx\n", pcChars); DbgPrint("\n PULONG pdxOut = %-#8lx\n", pdxOut ); DbgPrint("\n LPSIZE pSize = %-#8lx\n", pSize ); DbgPrint("\n )\n" ); #endif // Parameter validation. if ( ((pwsz == (LPWSTR) NULL) && (cwc != 0)) || (pSize == (LPSIZE) NULL) ) { WARNING("gdisrv!GreGetTextExtentExW(): invalid parameter\n"); SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER); return (FALSE); } // Early out. if (cwc == 0L) // Nothing to do, but we do not fail. { if ( pcChars != (COUNT *) NULL ) { *pcChars = 0; } return(TRUE); } // Lock the DC and set the new attributes. DCOBJ dco(hdc); if (!dco.bValid()) { WARNING("gdisrv!GreGetTextExtentExW(): invalid HDC\n"); SAVE_ERROR_CODE(ERROR_INVALID_HANDLE); } else { // Get the transform. EXFORMOBJ xo(dco, WORLD_TO_DEVICE); // Realize font and get the simulation flags and escapement. RFONTOBJ rfo(dco, FALSE, (fl & GTEEX_GLYPH_INDEX) ? RFONT_TYPE_HGLYPH : RFONT_TYPE_UNICODE); if (!rfo.bValid()) { WARNING("gdisrv!GreGetTextExtentExW(): could not lock HRFONT\n"); } else { if (rfo.prfnt->flType & RFONT_TYPE_HGLYPH) rfo.vFixUpGlyphIndices((USHORT *)pwsz, cwc); // If there is no pdxOut buffer provided, but we still need one to compute the // number of characters that fit (pcChars not NULL), then we will have to // allocate one of our own. #define DXOUTLEN 40 ULONG dxOut[DXOUTLEN]; PULONG pdxAlloc = NULL; if ((pdxOut == (PULONG) NULL) && (pcChars != (COUNT *) NULL)) { if (cwc <= DXOUTLEN) { pdxOut = &dxOut[0]; } else { if ((pdxAlloc = (PULONG) PALLOCMEM(cwc * sizeof(ULONG), 'txtG')) == (PULONG) NULL) { WARNING("gdisrv!GreGetTextExtentExW(): could not alloc temp buffer\n"); SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY); } pdxOut = pdxAlloc; } } // The STROBJ will now compute the text alignment, character positions, // and TextBox. ESTROBJ to( pwsz,cwc, dco, rfo, xo,PDXNULL,FALSE, // xo, PDXNULL, bPdy = FALSE dco.pdc->lEscapement(), dco.pdc->lTextExtra(), dco.pdc->lBreakExtra(), dco.pdc->cBreak(), 0,0,0,(LONG *) pdxOut ); if (to.bValid()) { // Transform the TextExtent to logical coordinates. Because this is a // new NT function, we need not worry about the extent compatibility hack. if (to.bTextExtent(rfo,0L,pSize)) { // Count number of characters that fit in the max. width. // If pcChars is NULL, we skip this and ignore the dxMax limit. if (pcChars && pdxOut) { ULONG c; for (c=0; cppal()); XEPALOBJ palDestDC(dco.ppal()); POINTL *pptlBO = &dco.pdc->ptlFillOrigin(); EBRUSHOBJ *peboText = dco.peboText(); EBRUSHOBJ *peboBackground = dco.peboBackground(); BOOL bDitherText = FALSE; if ( dco.flGraphicsCaps() & GCAPS_ARBRUSHTEXT ) { // Even if a printer driver sets GCAPS_ARBRUSHTEXT, we // can't allow vector fonts to be dithered. bDitherText = (!rfo.bPathFont() || rfo.bReturnsOutlines()); // We always dirty the text brush if ARBRUSHTEXT is set to // catch the cases where we transition between vector and // non-vector fonts but keep the same brush -- if we didn't // do this, we might try to use a cached, dithered brush // after switching from a TrueType font to a vector font. dco.ulDirtyAdd(DIRTY_TEXT); // Get through the are-you-really-dirty check in vInitBrush: peboText->vInvalidateUniqueness(); } if ( dco.bDirtyBrush(DIRTY_TEXT|DIRTY_BACKGROUND) ) { if ( dco.bDirtyBrush(DIRTY_TEXT) ) { peboText->vInitBrush(dco.pdc, gpbrText, palDestDC, palDest, pSurf, bDitherText); } if ( dco.bDirtyBrush(DIRTY_BACKGROUND) ) { peboBackground->vInitBrush( dco.pdc, gpbrBackground, palDestDC, palDest, pSurf, (dco.flGraphicsCaps() & GCAPS_ARBRUSHOPAQUE) ? TRUE : FALSE); } dco.vCleanBrush(DIRTY_TEXT|DIRTY_BACKGROUND); } // // Compute the clipping complexity and maybe reduce the exclusion // rectangle. The bounding rectangle must be converted to Screen // coordinates. // ERECTL rclExclude; rclExclude.left = prclBounds->left + ptlOrigin.x; rclExclude.right = prclBounds->right + ptlOrigin.x; rclExclude.top = prclBounds->top + ptlOrigin.y; rclExclude.bottom = prclBounds->bottom + ptlOrigin.y; ECLIPOBJ co( dco.prgnEffRao(), rclExclude, (rfo.prfnt->fobj.flFontType & FO_CLEARTYPE_X) ? CLIP_FORCE : CLIP_NOFORCE ); rclExclude = co.erclExclude(); // // Check the destination which is reduced by clipping. // if (!rclExclude.bEmpty()) { DEVEXCLUDEOBJ dxo(dco,&rclExclude,&co); // // We now begin the 'Big Loop'. We will pass thru this loop once for // each entry in the array of PolyText structures. Increment the // pSurface once before we enter. We assume success from here on out // unless we hit a failure. // INC_SURF_UNIQ(pSurf); PFN_DrvBitBlt pfnBitBlt = pSurf->pfnBitBlt(); PFN_DrvTextOut pfnTextOut; if ( ((rfo.prfnt->fobj.flFontType & FO_GRAY16) && !(dco.flGraphicsCaps() & GCAPS_GRAY16)) || (rfo.prfnt->fobj.flFontType & FO_CLEARTYPE_X) ) { pfnTextOut = SpTextOut; pSurf->pdcoAA = &dco; // make sure this is needed } else { pfnTextOut = pSurf->pfnTextOut(); } ERECTL rclInput; for (POLYTEXTW *ppt = lpto; ppt < lpto + nStrings; ppt += 1) { // // Process the rectangle in prcl. // rclInput.left = ppt->rcl.left + ptlOrigin.x; rclInput.right = ppt->rcl.right + ptlOrigin.x; rclInput.top = ppt->rcl.top + ptlOrigin.y; rclInput.bottom = ppt->rcl.bottom + ptlOrigin.y; // // Process the string. // if (ppt->n) { // // The STROBJ will now compute the text alignment, // character positions, and TextBox. // ESTROBJ to; to.vInitSimple( (PWSZ) ppt->lpstr, ppt->n, dco, rfo,ppt->x+ptlOrigin.x, ppt->y+ptlOrigin.y,NULL); if (to.bValid()) { // Draw the text. #ifdef FE_SB if( to.bLinkedGlyphs() ) { // If there are linked glyphs, then the bounds of the // glyphs (to.rclBkGround) might exceed the bounds of the // clipping object (co.rclBounds). If so, then we need // to increase the complexity of the clipping object from // DC_TRIVIAL to DC_RECT. if ((co.iDComplexity == DC_TRIVIAL) && ((to.rclBkGround.left < co.rclBounds.left) || (to.rclBkGround.right > co.rclBounds.right) || (to.rclBkGround.top < co.rclBounds.top) || (to.rclBkGround.bottom > co.rclBounds.bottom))) { co.iDComplexity = DC_RECT; } bProxyDrvTextOut ( dco, pSurf, to, co, (RECTL *) NULL, &rclInput, peboText, peboBackground, pptlBO, rfo, (PDEVOBJ*)NULL, (FLONG) 0L, &rclExclude ); } else { #endif (*pfnTextOut)(pSurf->pSurfobj(), (STROBJ *) &to, rfo.pfo(), &co, (RECTL *) NULL, &rclInput, peboText, peboBackground, pptlBO, (R2_COPYPEN | (R2_COPYPEN << 8))); } } else { bRet = FALSE; break; } } else { // intersect the dest rect with the clip rect and set it in co // we can only get away with touching co.rclBounds after // the ECLIPOBJ constructor because of two reasons: // a) the target rectangle passed to bitblt is contained in the // original bounds set by ECLIPOBJ, being the intersection // of the origianal bounds with THE intended target rectangle. // b) clipping complexity may have changed when we changed // co.erclExclude, but it only could have gotten simpler, // so at worst in those rare situations we would not go // through the optimal code path. // By changing clipping bounds we accomplish that no intersection // of the target rectangle with clipping region rectangle is emtpy // relieving the driver of extra work [bodind] co.erclExclude().left = max(rclExclude.left,rclInput.left); co.erclExclude().right = min(rclExclude.right,rclInput.right); co.erclExclude().top = max(rclExclude.top,rclInput.top); co.erclExclude().bottom = min(rclExclude.bottom,rclInput.bottom); // if not clipped, Just paint the rectangle. if ((co.erclExclude().left < co.erclExclude().right) && (co.erclExclude().top < co.erclExclude().bottom)) { (*pfnBitBlt)(pSurf->pSurfobj(), (SURFOBJ *) NULL, (SURFOBJ *) NULL, &co, NULL, &co.rclBounds, (POINTL *) NULL, (POINTL *) NULL, (BRUSHOBJ *)peboBackground, pptlBO, 0x0000f0f0); } co.erclExclude() = rclExclude; } } pSurf->pdcoAA = NULL; } } else { WARNING("gdisrv!GreExtTextOutW(): could not lock HRFONT\n"); bRet = FALSE; } } else { bRet = dco.bFullScreen(); } } else { bRet = FALSE; WARNING("Invalid DC passed to GreConsoleTextOut\n"); } return(bRet); } /******************************Public*Routine******************************\ * DWORD GreSetTextAlign (hdc,flOpts) * * * * Set the text alignment flags in the DC. * * * * History: * * * * Tue 28-Dec-1993 -by- Patrick Haluptzok [patrickh] * * smaller and faster * * * * 18-Dec-1990 -by- Donald Sidoroff [donalds] * * Wrote it. * \**************************************************************************/ UINT APIENTRY GreSetTextAlign(HDC hdc,UINT flOpts) { ULONG ulReturn = 0; XDCOBJ dco( hdc ); if(!dco.bValid()) { WARNING("Invalid DC or offset passed to GreSetTextAlign\n"); } else { ulReturn = (UINT)dco.pdc->lTextAlign(); dco.pdc->lTextAlign(flOpts); if (MIRRORED_DC(dco.pdc) && ((flOpts & TA_CENTER) != TA_CENTER)) { flOpts = flOpts ^ TA_RIGHT; } dco.pdc->flTextAlign(flOpts & (TA_UPDATECP | TA_CENTER | TA_BASELINE)); dco.vUnlockFast(); } return((UINT)ulReturn); } /******************************Public*Routine******************************\ * int GreSetTextCharacterExtra (hdc,lExtra) * * * * Sets the amount of intercharcter spacing for TextOut. * * * * History: * * Tue 28-Dec-1993 -by- Patrick Haluptzok [patrickh] * * smaller and faster * * * * Tue 08-Jan-1991 -by- Bodin Dresevic [BodinD] * * Update: bug, used to return lExtra instead of lOld, transform stuff * * deleted since it will be done at the TextOut time. * * * * 18-Dec-1990 -by- Donald Sidoroff [donalds] * * Wrote it. * \**************************************************************************/ int APIENTRY GreSetTextCharacterExtra(HDC hdc,int lExtra) { ULONG ulOld = 0x80000000; XDCOBJ dco( hdc ); if(!dco.bValid()) { WARNING("Invalid DC or offset passed to GreSetTextCharacterExtra\n"); } else { ulOld = dco.pdc->lTextExtra(); dco.pdc->lTextExtra(lExtra); dco.vUnlockFast(); } return(ulOld); } /******************************Public*Routine******************************\ * int GreGetTextCharacterExtra (hdc) * * * * Gets the amount of intercharcter spacing for TextOut. * * * * 29-Jun-1995 -by- Fritz Sands [fritzs] * * Wrote it. * \**************************************************************************/ int APIENTRY GreGetTextCharacterExtra(HDC hdc) { ULONG ulOld = 0; XDCOBJ dco( hdc ); if(!dco.bValid()) { WARNING("Invalid DC or offset passed to GreGetTextCharacterExtra\n"); } else { ulOld = dco.pdc->lTextExtra(); dco.vUnlockFast(); } return(ulOld); } /******************************Public*Routine******************************\ * * CalcJustInArray * * Effects: mimics win95 asm code, except that their code does not have * if (b_lpDx) clause, for all of their arrays are 16 bit. * * if GCP_JUSTIFYIN flag is set, the lpDx array on input contains * justifying priorities. * For latin this means the lpDx array will contain * 0's or 1's where 0 means that the glyph at this position can not be * used for spacing while 1 means that the glyph at this position should * be used for spacing. If GCP_JUSTIFYIN is NOT set, than space chars ' ', * in the input string are used to do justification. * * History: * 21-Jul-1995 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ DWORD nCalcJustInArray( UINT **ppJustIn, // place to store the pointer to array WCHAR Glyph, // glyph to find VOID *pvIn, // input array BOOL b_lpDx, // input array is lpDx or pwc UINT cGlyphs // length of the input array ) { WCHAR *pwc, *pwcEnd; int *pDx, *pDxEnd; int iGlyph; COUNT nJustIn = 0; UINT *piInit; // look for Glyph in the string if (b_lpDx) { iGlyph = (int)Glyph; pDxEnd = (int*)pvIn + cGlyphs; for (pDx = (int*)pvIn; pDx < pDxEnd; pDx++) { if (*pDx == iGlyph) nJustIn++; } } else { pwcEnd = (WCHAR*)pvIn + cGlyphs; for (pwc = (WCHAR*)pvIn; pwc < pwcEnd; pwc++) { if (*pwc == Glyph) nJustIn++; } } if ((nJustIn == 0) || // did not find any Glyphs in the string !(piInit = (UINT *)PALLOCMEM(nJustIn * sizeof(UINT), 'ylgG'))) { *ppJustIn = NULL; return 0; } // store locations where Glyph's are found in the input array UINT *pi = piInit; if (b_lpDx) { for (pDx = (int*)pvIn; pDx < pDxEnd; pDx++) { if (*pDx == iGlyph) { //Sundown: safe to truncate since pDxEnd = pvIn + cGlyphs *pi++ = (UINT)(pDx - (int*)pvIn); } } } else { for (pwc = (WCHAR*)pvIn; pwc < pwcEnd; pwc++) { if (*pwc == Glyph) { //Sundown: same as above *pi++ = (UINT)(pwc - (WCHAR*)pvIn); } } } // return the pointer with array of locations of uiGlyphs *ppJustIn = piInit; return nJustIn; } /******************************Public*Routine******************************\ * * VOID RFONTOBJ::vFixUpGlyphIndices(USHORT *pgi, UINT cgi) * * Effects: Windows 95 returns glyph indices for bitmap fonts that are the * same as ansi values. On NT glyph handles are zero based, so * we need to add chFirstChar to NT handles to get win95 indices * which is what we do in GetGlyphIndicesA/W and GetCharacterPlacement. * Conversely, when those indices are passed to us through * text routines we have to subtract chFirstChar from indices to * produce NT handles. This is what this routine does: * * History: * 04-Mar-1997 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ VOID RFONTOBJ::vFixUpGlyphIndices(USHORT *pgi, UINT cgi) { USHORT usFirst = prfnt->ppfe->pifi->chFirstChar; ASSERTGDI(prfnt->flType & RFONT_TYPE_HGLYPH, "vFixUpGlyphIndices\n"); ASSERTGDI(prfnt->ppfe->pfdg, "RFONTOBJ::vFixUpGlyphIndices invalid ppfe->pfdg \n"); if ((prfnt->ppfe->pfdg->flAccel & GS_8BIT_HANDLES) && usFirst) { // win95 does not return true glyph indicies but ansi // values for raster, vector, ps fonts for (USHORT *pgiEnd = pgi + cgi; pgi < pgiEnd; pgi++) *pgi -= usFirst; } } /******************************Public*Routine******************************\ * * GreGetGlyphIndicesW ( * * Effects: designed to emulate win95 behavior * * Warnings: * * History: * 25-Jul-1995 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ DWORD GreGetGlyphIndicesW ( HDC hdc, WCHAR *pwc, DWORD cwc, USHORT *pgi, DWORD iMode, BOOL bSubset ) { DWORD dwRet = GDI_ERROR; HGLYPH *phg, *phgInit; USHORT *pgiEnd = pgi + cwc; XDCOBJ dco(hdc); // Lock the DC. if (dco.bValid()) // Check if it's good. { // Locate the RFONT. // It might be better to set the type to RFONT_TYPE_HGLYPH // in anticipation of ETO_GLYPH_INDEX ExtTextOut calls RFONTOBJ rfo(dco, FALSE, RFONT_TYPE_UNICODE); if (rfo.bValid()) { USHORT usFirst = rfo.prfnt->ppfe->pifi->chFirstChar; // if cwc == 0 all we are care for is the # of distinct glyph indices if (cwc==0) { ASSERTGDI(iMode == 0, "GreGetGlyphIndicesW parameters bogus\n"); if (rfo.prfnt->ppfe->pifi->cjIfiExtra > offsetof(IFIEXTRA,cig)) { dwRet = ((IFIEXTRA *)(rfo.prfnt->ppfe->pifi + 1))->cig; } else { dwRet = 0; } } else { // if we ever switch to 16 bit HGLYPHS in ddi, this alloc will no // longer be necessary [bodind] if (phgInit = (phg = (HGLYPH *)PALLOCMEM(cwc * sizeof(HGLYPH), 'ylgG'))) { rfo.vXlatGlyphArray(pwc, cwc, phg, iMode, bSubset); // separate loops for faster processing: ASSERTGDI(rfo.prfnt->ppfe->pfdg, "GreGetGlyphIndicesW invalid ppfe->pfdg \n"); if (rfo.prfnt->ppfe->pfdg->flAccel & (GS_8BIT_HANDLES|GS_16BIT_HANDLES)) { if ((rfo.prfnt->ppfe->pfdg->flAccel & GS_8BIT_HANDLES) && usFirst) { // win95 does not return true glyph indicies but ansi // values for raster, vector, ps fonts for ( ; pgi < pgiEnd; pgi++, pwc++, phg++) *pgi = (USHORT)*phg + usFirst; } else { for ( ; pgi < pgiEnd; pgi++, pwc++, phg++) *pgi = (USHORT)*phg; } dwRet = cwc; // can not fail any more } else { dwRet = GDI_ERROR; } VFREEMEM(phgInit); } } } dco.vUnlockFast(); } return dwRet; } /******************************Public*Routine******************************\ * * DWORD GreGetCharacterPlacementW * * * * History: * 06-Jan-1995 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ DWORD GreGetCharacterPlacementW( HDC hdc, LPWSTR pwsz, DWORD nCountIn, DWORD nMaxExtent, LPGCP_RESULTSW pResults, DWORD dwFlags ) { SIZE size; GCP_RESULTSW gcpw; DWORD nCount = nCountIn; DWORD dwWidthType = 0; int *pDx = NULL; UINT *pJustIn = NULL; DWORD dwJustInOff = 0; // used by calc routine for spacing DWORD nJustIn = 0; ULONG nKern = 0; KERNINGPAIR *pKern = NULL; KERNINGPAIR *pKernSave = NULL; // esential initialization int nExtentLeft = 0; int nExtentRem = 0; WORD *pwc,*pwcEnd; DWORD i, j; // init size size.cx = size.cy = 0; // we will only be implementing the simple version of this api, // that is for now we will not be calling LPK dlls if (!pResults) { if (!GreGetTextExtentW(hdc, (LPWSTR)pwsz, (int)nCount, &size, GGTE_WIN3_EXTENT)) { WARNING("GreGetCharacterPlacementW, GreGetTextExtentW failed\n"); return 0; } // now do unthinkable win95 stuff, chop off 32 bit values to 16 bits return (DWORD)((USHORT)size.cx) | (DWORD)(size.cy << 16); } // main code starts here. We are following win95 code as closely as possible. // Copy pResults to the stack, for faster access I presume. gcpw = *pResults; // take nCount to be the smaller of the nCounts and nGlyphs if (nCount > gcpw.nGlyphs) nCount = gcpw.nGlyphs; // Calc pJustIn array if any if (dwFlags & GCP_JUSTIFY) // if have this dwFlags |= GCP_MAXEXTENT; // then must also have this if ((dwFlags & GCP_JUSTIFYIN) && gcpw.lpDx) { // if this flag is set, the lpDx array on input contains // justifying priorities so we can not continue if lpDx is not present. // For latin this means the lpDx array will contain // 0's or 1's where 0 means that the glyph at this position can not be // used for spacing while 1 means that the glyph at this position should // be used for spacing. If GCP_JUSTIFYIN is NOT set, than space chars ' ', // in the string are used to do justification. // Now that we have everything in place we can call CalcJustInArray // to compute the array in pJustIn nJustIn = nCalcJustInArray(&pJustIn, 1, (VOID *)gcpw.lpDx, TRUE ,gcpw.nGlyphs); if (!nJustIn) { // if this computation fails must pretend that this flag is not set dwFlags &= ~GCP_JUSTIFYIN; } } else { // either GCP_JUSTIFYIN not set or lpDx is NULL input, // in either case can kill the bit dwFlags &= ~GCP_JUSTIFYIN; } // we could have either the lpDx, lpCaretPos or neither or both. Take this // into account and call GetTextExtentEx // we could or could not be asking for a maximum. If we are, put it in the // local version of the results structure. if (gcpw.lpDx) dwWidthType += 1; // bogus way of doing it, i.e. win95 way if (gcpw.lpCaretPos) dwWidthType += 2; // bogus way of doing it, i.e. win95 way // dwWidthType can be 0,1,2,3 pDx = gcpw.lpDx; if (dwWidthType == 2) // CaretPos only pDx = gcpw.lpCaretPos; // reset the array pointer // Check if the count should be reduced even further COUNT *pnCount = NULL; if (dwFlags & GCP_MAXEXTENT) { pnCount = (COUNT*)&nCount; } // now call GetTextExtentEx if (!GreGetTextExtentExW( hdc, // device context (LPWSTR)pwsz, // pointer to a UNICODE text string nCount, // count of WCHARs in the string (ULONG)nMaxExtent, // maximum width to return pnCount, // number of chars that fit in dxMax (PULONG)pDx, // offset of each character from string origin &size,0)) { if (pJustIn) VFREEMEM(pJustIn); return 0; } // these few lines of code do not exist in Win95, presumably because // their internal version of GetTextExtentExW does not return positions // of glyphs (relative to the first glyph) but character increments along // baseline. if (pDx && (nCount > 0)) { for (int * pDxEnd = &pDx[nCount - 1]; pDxEnd > pDx; pDxEnd--) { *pDxEnd -= pDxEnd[-1]; } } if ((dwFlags & GCP_MAXEXTENT) && (nCount == 0)) { if (pJustIn) VFREEMEM(pJustIn); return (DWORD)((USHORT)size.cx) | (DWORD)(size.cy << 16); } // Kerning: // It only makes sense to do kerning if there are more than 2 glyphs if ((dwFlags & GCP_USEKERNING) && (dwWidthType != 0) && (nCount >= 2)) { // Get the number of kerning pairs, if zero done if (nKern = GreGetKerningPairs(hdc,0,NULL)) { if (pKernSave = (KERNINGPAIR*)PALLOCMEM(nKern * sizeof(KERNINGPAIR), 'txtG')) { // consistency check for GetKerningPairs: if (GreGetKerningPairs(hdc,nKern,pKernSave) != nKern) { // something is gone wrong, out of here if (pJustIn) VFREEMEM(pJustIn); if (pKernSave) VFREEMEM(pKernSave); return 0; } KERNINGPAIR *pKernEnd = pKernSave + nKern; for (pKern = pKernSave; pKern < pKernEnd; pKern++) { // now go over the sting and find all the instances of // THIS kerning pair in the string: register WORD wcFirst = pKern->wFirst; // note that this is the loop for trying the wcFirst // so that the end condition is &pwsz[nCount - 2], // wcSecond could go up to &pwsz[nCount - 1], // Either I do not understand Win95 code or they have // a bug in that they could fault on trying to access the // the second glyph in a pair in the input string pwcEnd = (WORD*)pwsz + (nCount - 1); for (pwc = (WORD *)pwsz; pwc < pwcEnd; pwc++) { if ((wcFirst == pwc[0]) && (pwc[1] == pKern->wSecond)) { // found a kerning pair in the string, // we need to modify the pDx vector for the second // glyph in the kerning pair pDx[pwc - (WORD *)pwsz] += pKern->iKernAmount; // also adjust the return value accordingly: size.cx += pKern->iKernAmount; } } } // on to the next pair // done with kerning pairs can free the memory VFREEMEM(pKernSave); // if we have kerned positive amounts, then the string could well // have gone over the preset limit. If so, reduce the number of // characters we found [win95 comment] if (dwFlags & GCP_MAXEXTENT) { while (((DWORD)size.cx > nMaxExtent) && (nCount > 0)) { // point to the last glyph in the string size.cx -= pDx[nCount - 1]; nCount -= 1; } // see if there are any glyphs left to process if (nCount == 0) { if (pJustIn) VFREEMEM(pJustIn); pResults->nGlyphs = nCount; pResults->nMaxFit = (int)nCount; return 0; } } } // no memory } // no kern pairs } // Justification, check flags and the presence of array if ((dwFlags & GCP_JUSTIFY) && dwWidthType && (nCount > 0)) { int *pDxEnd = &pDx[nCount - 1]; // must have nCount > 0 for this // check trailing spaces, adjust nCount further to remove trailing spaces for ( pwcEnd = (WORD*)&pwsz[nCount-1]; (pwcEnd >= (WORD*)pwsz) && (*pwcEnd == L' '); nCount--, pwcEnd--, pDxEnd-- ) { size.cx -= *pDxEnd; } if (nCount == 0) { if (pJustIn) VFREEMEM(pJustIn); pResults->nGlyphs = nCount; pResults->nMaxFit = (int)nCount; return 0; } // See if we need to justify. // Can not justify one character, need at least two... nExtentLeft = (int)nMaxExtent - (int)size.cx; if ((nExtentLeft >= 0) && (nCount >= 2)) { // ... yes, we do need to justify // if GCP_JUSTIFYIN was set, pJustIn and nJustIn have // already been set. if (!nJustIn) // try to use ' ' as a "spacer glyph" { nJustIn = nCalcJustInArray(&pJustIn,L' ', (VOID *)pwsz, FALSE, nCount); } if (nJustIn) { // Make sure that the array doesn't say to // space a character beyond the new nCount. j = nCount - 1; // convert count to index of the last glyph int ii; for (ii = (int)(nJustIn - 1); ii >= 0; ii--) { if ((UINT)j >= pJustIn[ii]) break; } // pJustIn array is zero based, to get the effective number // of "spacers" in the array must add 1 to the index of the last "spacer" in the array i = (DWORD)ii + 1; // run a sort of primitive DDA a'la DavidMS nExtentRem = (int) (((DWORD)nExtentLeft) % i); nExtentLeft /= i; for (j = 0; j < i; j++, nExtentRem--) { int dxCor = nExtentLeft; if (nExtentRem > 0) dxCor += 1; pDx[pJustIn[j]] += dxCor; } } else { // no spaces. justify by expanding every character evenly. while (nExtentLeft > 0) { // Note the end condition: nCount - 1, rather // than usual nCount; This is because there is no point // in adding spaces to the last glyph in the string j = nCount - 1; for (i = 0; i < j; i++) { pDx[i] += 1; if (!(--nExtentLeft)) break; } } } } #if DBG else { if (nCount < 2) RIP("GetCharacterPlacement, justification wrong\n"); } #endif // we now have exactly this: size.cx = (LONG)nMaxExtent; } // fill other width array, that is CaretPos if (dwWidthType == 3) // both lpDx and lpCaretPos are non NULL RtlCopyMemory(gcpw.lpCaretPos, gcpw.lpDx, nCount * sizeof(int)); // caret positioning is from the start of the string, // not the previous character. if (gcpw.lpCaretPos) { int iCaretPos = 0, iDx = 0; for (i = 0; i < nCount; i++) { iDx = gcpw.lpCaretPos[i]; gcpw.lpCaretPos[i] = iCaretPos; iCaretPos += iDx; } } // Fix Output String if (gcpw.lpOutString) RtlCopyMemory(gcpw.lpOutString, pwsz, nCount * sizeof(WCHAR)); // Classification if (gcpw.lpClass) RtlFillMemory(gcpw.lpClass, nCount, GCPCLASS_LATIN); // Ordering if (gcpw.lpOrder) { for (i = 0; i < nCount; i++) { gcpw.lpOrder[i] = i; } } // Get lpGlyphs if (gcpw.lpGlyphs) { if (GreGetGlyphIndicesW( hdc,(WCHAR*)pwsz,nCount, (USHORT*)gcpw.lpGlyphs,0, FALSE) == GDI_ERROR) { nCount = 0; size.cx = size.cy = 0; } } // Finally fix counters in the returned structure if (pJustIn) VFREEMEM(pJustIn); pResults->nGlyphs = nCount; pResults->nMaxFit = nCount; return (DWORD)((USHORT)size.cx) | (DWORD)(size.cy << 16); } /**************************************************************************\ * NtGdiGetWidthTable * * Gets a table of character advance widths for a font. Returns SHORTs * over the C/S interface to save space. (Note that 99+% of all requests * will be happy with this limitation.) * * We will try real hard to get widths for the "special" characters at the * start of the array. Other widths are returned only when they are not * expensive. (Expensive is True Type rasterizing a glyph, for example.) * The value NO_WIDTH is returned for those expensive glyphs. * * We return GDI_ERROR (0xFFFFFFFF) in case of an error. TRUE indicates * that all widths were easy. * * History: * Tue 13-Jun-1995 22:24:49 by Gerrit van Wingerden [gerritv] * Moved to kernel mode * * Mon 11-Jan-1993 22:24:39 -by- Charles Whitmer [chuckwh] * Wrote it. Sorry about the wierd structure, I'm trying to get good tail * merging. \**************************************************************************/ BOOL APIENTRY NtGdiGetWidthTable ( HDC hdc, // Device context ULONG cSpecial, WCHAR *pwc, // Pointer to a UNICODE text codepoints. ULONG cwc, // Count of chars. USHORT *psWidth, // Width table (returned). WIDTHDATA *pwd, // Useful font data (returned). FLONG *pflInfo // Font info flags. ) { ULONG ii; BOOL bRet = (BOOL) GDI_ERROR; XDCOBJ dco(hdc); // Lock the DC. if (dco.bValid()) // Check if it's good. { WIDTHDATA wd; FLONG flInfo; USHORT *psWidthTmp = NULL; WCHAR *pwcTmp; if (!BALLOC_OVERFLOW2(cwc, USHORT, WCHAR)) { psWidthTmp = (USHORT*) AllocFreeTmpBuffer(cwc*(sizeof(USHORT)+sizeof(WCHAR))); } if( psWidthTmp ) { pwcTmp = (WCHAR*) (psWidthTmp + cwc); __try { ProbeForRead(pwc,sizeof(WCHAR)*cwc,sizeof(WORD)); RtlCopyMemory(pwcTmp,pwc,cwc*sizeof(WCHAR)); } __except(EXCEPTION_EXECUTE_HANDLER) { cwc = 0; } } else { WARNING("NtGdiGetWidthTable unable to allocate memory\n"); cwc = 0; } if (cwc) { // Locate the RFONT. RFONTOBJ rfo(dco,FALSE); if (rfo.bValid()) { // Grab the flInfo flags. flInfo = rfo.flInfo(); if (rfo.cxMax() < 0xFFF) { // Check for a simple case. We still have to fill the table // because some one may want it. if (rfo.lCharInc()) { USHORT *psWidth1 = psWidthTmp; LONG fxInc = rfo.lCharInc() << 4; for (ii=0; iiulMapMode() == MM_TEXT) && (dco.ulDirty() & DISPLAY_DC)) { // Get the hfont and check if it is public hf = dco.pdc->hlfntNew(); if ((hf != NULL) && GreGetObjectOwner((HOBJ)hf,LFONT_TYPE) == OBJECT_OWNER_PUBLIC) { RFONTOBJ rfo(dco,FALSE); if (rfo.bValid() && (rfo.cxMax() < 0xFFF)) { // lets see if we can find an available cpf for (ii = 0; ii < MAX_PUBLIC_CFONT; ++ii) { if (gpGdiSharedMemory->acfPublic[ii].hf == 0) break; if (gpGdiSharedMemory->acfPublic[ii].hf == (HFONT)hf) { WARNING("iGetPublicWidthTable - font already in public list\n"); ii = MAX_PUBLIC_CFONT; } } if (ii < MAX_PUBLIC_CFONT) { PCFONT pcf = &gpGdiSharedMemory->acfPublic[ii]; pcf->timeStamp = gpGdiSharedMemory->timeStamp; // Grab the flInfo flags. pcf->flInfo = rfo.flInfo(); // Check for a simple case. We still have to fill the table // because some one may want it. if (rfo.lCharInc()) { USHORT usInc = (USHORT)(rfo.lCharInc() << 4); for (ii=0; ii<256; ii++) pcf->sWidth[ii] = usInc; bRet = TRUE; } else { WCHAR wch[256]; LFONTOBJ lfo(hf); if (lfo.bValid()) { if (IS_ANY_DBCS_CHARSET(lfo.plfw()->lfCharSet)) { ULONG uiCodePage = ulCharsetToCodePage((UINT)lfo.plfw()->lfCharSet); for (ii = 0; ii < 256; ++ii) { UCHAR j = (UCHAR) ii; EngMultiByteToWideChar(uiCodePage, &wch[ii], sizeof(WCHAR), (LPSTR)&j, 1); } } else { UCHAR ach[256]; ULONG BytesReturned; for (ii = 0; ii < 256; ++ii) ach[ii] = (UCHAR)ii; RtlMultiByteToUnicodeN(wch, sizeof(wch), &BytesReturned, (PCHAR)ach, sizeof(ach)); } bRet = rfo.bGetWidthTable(dco,256,wch,256,pcf->sWidth); if (bRet == GDI_ERROR) bRet = FALSE; } } // If things are going well, get the WIDTHDATA , metrics and // RealizationInfo DCOBJ dcof(hdc); if (bRet && rfo.bGetWidthData(&pcf->wd,dco) && #ifdef LANGPACK rfo.GetRealizationInfo(&pcf->ri) && #endif bGetTextMetrics(rfo, dcof, &pcf->tmw) ) { pcf->fl = CFONT_COMPLETE | CFONT_CACHED_METRICS | CFONT_CACHED_WIDTHS | #ifdef LANGPACK CFONT_CACHED_RI | #endif CFONT_PUBLIC; // since we are always MM_TEXT, the xform is identity pcf->efM11 = ef_16; pcf->efM22 = ef_16; pcf->efDtoWBaseline = ef_1_16; pcf->efDtoWAscent = ef_1_16; pcf->hf = (HFONT)hf; pcf->hdc = 0; pcf->cRef = 0; pcf->lHeight = FXTOL((LONG) pcf->wd.sHeight); } else { // if got error... we should retuen MAX_PUBLIC_CFONT.. // At this point "ii" is not equal to MAX_PUBLIC_CFONT, it // points first free entry of cache table... // Just force set it to error.. ii = MAX_PUBLIC_CFONT; } } } else { WARNING("gdisrv!GreGetWidthTable(): could not lock HRFONT\n"); } } } dco.vUnlockFast(); } return(ii); } /******************************Public*Routine******************************\ * NtGdiSetupPublicCFONT() * * Modify the cached public cfont. * * if HDC is non-null, textmetrics must be set * if HF is non-null, cached ave width must be set * * returns index of cfont modified * * History: * 23-Feb-1996 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ int APIENTRY NtGdiSetupPublicCFONT( HDC hdc, HFONT hf, ULONG ulAve) { int ii = MAX_PUBLIC_CFONT; // get metrics and widths if necesary if (hdc) { ii = iGetPublicWidthTable(hdc); } // now see if we need to fill in the ave width if (hf) { // if we havn't found it yet, find it if (ii == MAX_PUBLIC_CFONT) { for (ii = 0; ii < MAX_PUBLIC_CFONT; ++ii) { if (gpGdiSharedMemory->acfPublic[ii].hf == hf) break; } } if (ii < MAX_PUBLIC_CFONT) { gpGdiSharedMemory->acfPublic[ii].ulAveWidth = ulAve; gpGdiSharedMemory->acfPublic[ii].fl |= CFONT_CACHED_AVE; } } return(ii); } UINT APIENTRY GreGetTextAlign(HDC hdc) { XDCOBJ dco( hdc ); if(dco.bValid()) { UINT uTextAlign = dco.pdc->lTextAlign(); dco.vUnlockFast(); return uTextAlign; } else { WARNING("GreGetTextAlign: invalid DC\n"); return(0); } }