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

592 lines
23 KiB

  1. /******************************Module*Header**********************************\
  2. *
  3. * *******************
  4. * * GDI SAMPLE CODE *
  5. * *******************
  6. *
  7. * Module Name: textout.c
  8. *
  9. * Content:
  10. *
  11. * glyph rendering module. Uses glyph caching for P3 and
  12. * glyph expansion for older Glint series accelerators.
  13. *
  14. * Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved.
  15. * Copyright (c) 1995-2003 Microsoft Corporation. All rights reserved.
  16. \*****************************************************************************/
  17. //@@BEGIN_DDKSPLIT
  18. //
  19. // There are three basic methods for drawing text with hardware
  20. // acceleration:
  21. //
  22. // 1) Glyph caching -- Glyph bitmaps are cached by the accelerator
  23. // (probably in off-screen memory), and text is drawn by
  24. // referring the hardware to the cached glyph locations.
  25. //
  26. // 2) Glyph expansion -- Each individual glyph is colour-expanded
  27. // directly to the screen from the monochrome glyph bitmap
  28. // supplied by GDI.
  29. //
  30. // 3) Buffer expansion -- The CPU is used to draw all the glyphs into
  31. // a 1bpp monochrome bitmap, and the hardware is then used
  32. // to colour-expand the result.
  33. //
  34. // The fastest method depends on a number of variables, such as the
  35. // colour expansion speed, bus speed, CPU speed, average glyph size,
  36. // and average string length.
  37. //
  38. // For the S3 with normal sized glyphs, I've found that caching the
  39. // glyphs in off-screen memory is typically the slowest method.
  40. // Buffer expansion is typically fastest on the slow ISA bus (or when
  41. // memory-mapped I/O isn't available on the x86), and glyph expansion
  42. // is best on fast buses such as VL and PCI.
  43. //
  44. // Glyph expansion is typically faster than buffer expansion for very
  45. // large glyphs, even on the ISA bus, because less copying by the CPU
  46. // needs to be done. Unfortunately, large glyphs are pretty rare.
  47. //
  48. // An advantange of the buffer expansion method is that opaque text will
  49. // never flash -- the other two methods typically need to draw the
  50. // opaquing rectangle before laying down the glyphs, which may cause
  51. // a flash if the raster is caught at the wrong time.
  52. //
  53. //@@END_DDKSPLIT
  54. #include "precomp.h"
  55. #include "pxrx.h"
  56. //*********************************************************************************************
  57. // FUNC: vPxRxClipSolid
  58. // ARGS: ppdev (I) - pointer to physical device object
  59. // crcl (I) - number of rectangles
  60. // prcl (I) - array of rectangles
  61. // iColor (I) - the solid fill color
  62. // pco (I) - pointer to the clip region object
  63. // RETN: void
  64. //---------------------------------------------------------------------------------------------
  65. // Fill a series of rectangles clipped by pco with a solid color. This function should only
  66. // be called when the clipping operation is non-trivial
  67. //*********************************************************************************************
  68. VOID vPxRxClipSolid(PDEV* ppdev, LONG crcl, RECTL* prcl, ULONG iColor, CLIPOBJ* pco)
  69. {
  70. BOOL bMore; // Flag for clip enumeration
  71. CLIPENUM ce; // Clip enumeration object
  72. ULONG i;
  73. ULONG j;
  74. RECTL arclTmp[4];
  75. ULONG crclTmp;
  76. RECTL* prclTmp;
  77. RECTL* prclClipTmp;
  78. LONG iLastBottom;
  79. RECTL* prclClip;
  80. RBRUSH_COLOR rbc;
  81. GLINT_DECL;
  82. ASSERTDD((crcl > 0) && (crcl <= 4), "Expected 1 to 4 rectangles");
  83. ASSERTDD((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL), "Expected a non-null clip object");
  84. rbc.iSolidColor = iColor;
  85. if (pco->iDComplexity == DC_RECT)
  86. {
  87. crcl = cIntersect(&pco->rclBounds, prcl, crcl);
  88. if (crcl != 0)
  89. {
  90. ppdev->pgfnFillSolid(ppdev, crcl, prcl, __GLINT_LOGICOP_COPY,
  91. __GLINT_LOGICOP_COPY, rbc, NULL);
  92. }
  93. }
  94. else // iDComplexity == DC_COMPLEX
  95. {
  96. // Bottom of last rectangle to fill
  97. iLastBottom = prcl[crcl - 1].bottom;
  98. CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
  99. do {
  100. bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (VOID*)&ce);
  101. for (j = ce.c, prclClip = ce.arcl; j-- > 0; prclClip++)
  102. {
  103. // Since the rectangles and the region enumeration are both
  104. // right-down, we can zip through the region until we reach
  105. // the first fill rect, and are done when we've passed the
  106. // last fill rect.
  107. if (prclClip->top >= iLastBottom)
  108. {
  109. return; // Past last fill rectangle; nothing left to do
  110. }
  111. if (prclClip->bottom > prcl->top)
  112. {
  113. // We've reached the top Y scan of the first rect, so
  114. // it's worth bothering checking for intersection.
  115. prclTmp = prcl;
  116. prclClipTmp = arclTmp;
  117. for (i = crcl, crclTmp = 0; i--; prclTmp++)
  118. {
  119. // Intersect fill and clip rectangles
  120. if (bIntersect(prclTmp, prclClip, prclClipTmp))
  121. {
  122. // Add to list if anything's left to draw:
  123. crclTmp++;
  124. prclClipTmp++;
  125. }
  126. }
  127. // Draw the clipped rects
  128. if (crclTmp)
  129. {
  130. ppdev->pgfnFillSolid(ppdev, crclTmp, &arclTmp[0],
  131. __GLINT_LOGICOP_COPY, __GLINT_LOGICOP_COPY, rbc, NULL);
  132. }
  133. }
  134. }
  135. } while(bMore);
  136. }
  137. }
  138. //*********************************************************************************************
  139. // FUNC: bPxRxUncachedText
  140. // ARGS: ppdev (I) - pointer to physical device object
  141. // pgp (I) - array of glyphs to render
  142. // cGlyph (I) - number of glyphs to render
  143. // ulCharInc (I) - fixed character spacing increment (0 if proportional font)
  144. // RETN: TRUE if glyphs were rendered
  145. //---------------------------------------------------------------------------------------------
  146. // Renders an array of proportional or monospaced glyphs. This function requires RasterizerMode
  147. // to be set-up to correctly byteswap and mirror bitmasks.
  148. // NB. currently render to cxGlyphAligned rather than cxGlyph, this saves a lot of work on the
  149. // host but probably costs, on average, four bits per glyph row; as this is a fallback
  150. // routine I've not investigated whether this method is optimal.
  151. //*********************************************************************************************
  152. BOOL bPxRxUncachedText(PDEV* ppdev, GLYPHPOS* pgp, LONG cGlyph, ULONG ulCharInc)
  153. {
  154. GLYPHBITS *pgb;
  155. LONG cxGlyph, cyGlyph, cxGlyphAligned;
  156. LONG x, y;
  157. ULONG *pulGlyph;
  158. LONG cjGlyph;
  159. LONG culGlyph;
  160. LONG cjGlyphRem;
  161. LONG cj;
  162. ULONG ul;
  163. GLINT_DECL;
  164. DISPDBG((7, "bPxRxUncachedText: entered"));
  165. if (ulCharInc)
  166. {
  167. x = pgp->ptl.x + pgp->pgdf->pgb->ptlOrigin.x - ulCharInc;
  168. y = pgp->ptl.y + pgp->pgdf->pgb->ptlOrigin.y;
  169. }
  170. for ( ; --cGlyph >= 0; ++pgp)
  171. {
  172. pgb = pgp->pgdf->pgb;
  173. if (ulCharInc)
  174. {
  175. x += ulCharInc;
  176. }
  177. else
  178. {
  179. x = pgp->ptl.x + pgb->ptlOrigin.x;
  180. y = pgp->ptl.y + pgb->ptlOrigin.y;
  181. }
  182. cyGlyph = pgb->sizlBitmap.cy;
  183. cxGlyph = pgb->sizlBitmap.cx;
  184. cxGlyphAligned = ((cxGlyph + 7 ) & ~7);
  185. // Render2D turns on FastFillEnable which is incompatible with PackedBitMasks
  186. WAIT_PXRX_DMA_TAGS(4);
  187. QUEUE_PXRX_DMA_TAG( __GlintTagRectanglePosition, MAKEDWORD_XY(x, y));
  188. QUEUE_PXRX_DMA_TAG( __GlintTagRender2D, __RENDER2D_INCX | __RENDER2D_INCY |
  189. __RENDER2D_OP_SYNCBITMASK |
  190. __RENDER2D_WIDTH(cxGlyphAligned) |
  191. __RENDER2D_HEIGHT(0));
  192. QUEUE_PXRX_DMA_TAG(__GlintTagCount, cyGlyph);
  193. QUEUE_PXRX_DMA_TAG(__GlintTagRender, __RENDER_TRAPEZOID_PRIMITIVE |
  194. __RENDER_SYNC_ON_BIT_MASK);
  195. pulGlyph = (ULONG *)pgb->aj;
  196. cjGlyph = (cxGlyphAligned >> 3) * cyGlyph;
  197. culGlyph = cjGlyph >> 2;
  198. cjGlyphRem = cjGlyph & 3;
  199. ul = culGlyph + (cjGlyphRem != 0);
  200. WAIT_PXRX_DMA_DWORDS(ul + 1);
  201. QUEUE_PXRX_DMA_HOLD(__GlintTagBitMaskPattern, ul);
  202. for ( ; --culGlyph >= 0; ++pulGlyph)
  203. {
  204. QUEUE_PXRX_DMA_DWORD(*pulGlyph);
  205. }
  206. if (cjGlyphRem)
  207. {
  208. for (ul = cj = 0; cj < cjGlyphRem; ++cj, ++(BYTE *)pulGlyph)
  209. {
  210. ul |= ((ULONG)(*(BYTE *)pulGlyph)) << (cj << 3);
  211. }
  212. QUEUE_PXRX_DMA_DWORD(ul);
  213. }
  214. }
  215. // The rasterizer's set-up to expect a continue after each Render command (NB. but not Render2D, etc),
  216. // so it won't flush the text to the framebuffer unless we specifically tell it to
  217. WAIT_PXRX_DMA_TAGS(1);
  218. QUEUE_PXRX_DMA_TAG(__GlintTagContinueNewSub, 0);
  219. DISPDBG((7, "bPxRxUncachedText: exited"));
  220. return(TRUE);
  221. }
  222. //*********************************************************************************************
  223. // FUNC: bPxRxUncachedClippedText
  224. // ARGS: ppdev (I) - pointer to physical device object
  225. // pgp (I) - array of glyphs to render
  226. // cGlyph (I) - number of glyphs to render
  227. // ulCharInc (I) - fixed character spacing increment (0 if proportional font)
  228. // pco (I) - pointer to the clip region object
  229. // RETN: TRUE if glyphs were rendered
  230. //---------------------------------------------------------------------------------------------
  231. // Renders an array of proportional or monospaced glyphs. This function requires RasterizerMode
  232. // to be set-up to correctly byteswap and mirror bitmasks.
  233. // NB. currently render to cxGlyphAligned rather than cxGlyph, this saves a lot of work on the
  234. // host but probably costs, on average, four bits per glyph row; as this is a fallback
  235. // routine I've not investigated whether this method is optimal.
  236. //*********************************************************************************************
  237. BOOL bPxRxUncachedClippedText(PDEV* ppdev, GLYPHPOS* pgp, LONG cGlyph, ULONG ulCharInc, CLIPOBJ *pco)
  238. {
  239. GLYPHBITS *pgb;
  240. LONG cxGlyph, cyGlyph, cxGlyphAligned;
  241. LONG x, y;
  242. ULONG *pulGlyph;
  243. LONG cjGlyph;
  244. LONG culGlyph;
  245. LONG cjGlyphRem;
  246. LONG cj;
  247. ULONG ul;
  248. LONG cGlyphOriginal = 0;
  249. GLYPHPOS *pgpOriginal = NULL;
  250. BOOL bMore, invalidatedScissor = FALSE;
  251. CLIPENUM ce;
  252. RECTL *prclClip;
  253. BOOL bClipSet;
  254. GLINT_DECL;
  255. DISPDBG((7, "bPxRxUncachedClippedText: entered"));
  256. if (pco->iDComplexity == DC_RECT)
  257. {
  258. bMore = FALSE;
  259. ce.c = 1;
  260. prclClip = &pco->rclBounds;
  261. goto SingleRectangle;
  262. }
  263. cGlyphOriginal = cGlyph;
  264. pgpOriginal = pgp;
  265. CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
  266. do {
  267. bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (ULONG*) &ce);
  268. for (prclClip = &ce.arcl[0]; ce.c != 0; ce.c--, prclClip++)
  269. {
  270. cGlyph = cGlyphOriginal;
  271. pgp = pgpOriginal;
  272. SingleRectangle:
  273. bClipSet = FALSE;
  274. if (ulCharInc)
  275. {
  276. x = pgp->ptl.x + pgp->pgdf->pgb->ptlOrigin.x - ulCharInc;
  277. y = pgp->ptl.y + pgp->pgdf->pgb->ptlOrigin.y;
  278. }
  279. for ( ; --cGlyph >= 0; ++pgp)
  280. {
  281. pgb = pgp->pgdf->pgb;
  282. if(ulCharInc)
  283. {
  284. x += ulCharInc;
  285. }
  286. else
  287. {
  288. x = pgp->ptl.x + pgb->ptlOrigin.x;
  289. y = pgp->ptl.y + pgb->ptlOrigin.y;
  290. }
  291. cyGlyph = pgb->sizlBitmap.cy;
  292. cxGlyph = pgb->sizlBitmap.cx;
  293. cxGlyphAligned = ((cxGlyph + 7 ) & ~7);
  294. if ((prclClip->right > x) && (prclClip->bottom > y) &&
  295. (prclClip->left < x + cxGlyph) && (prclClip->top < y + cyGlyph))
  296. {
  297. // Lazily set the hardware clipping:
  298. if(!bClipSet)
  299. {
  300. WAIT_PXRX_DMA_TAGS(3);
  301. QUEUE_PXRX_DMA_TAG(__GlintTagScissorMinXY, (prclClip->top << SCISSOR_YOFFSET) |
  302. (prclClip->left << SCISSOR_XOFFSET));
  303. QUEUE_PXRX_DMA_TAG(__GlintTagScissorMaxXY, (prclClip->bottom << SCISSOR_YOFFSET) |
  304. (prclClip->right << SCISSOR_XOFFSET));
  305. QUEUE_PXRX_DMA_TAG(__GlintTagScissorMode, (USER_SCISSOR_ENABLE | SCREEN_SCISSOR_DEFAULT));
  306. invalidatedScissor = TRUE;
  307. bClipSet = TRUE;
  308. }
  309. // Render2D turns on FastFillEnable which is incompatible with PackedBitMasks
  310. WAIT_PXRX_DMA_TAGS(4);
  311. QUEUE_PXRX_DMA_TAG(__GlintTagRectanglePosition, MAKEDWORD_XY(x, y));
  312. QUEUE_PXRX_DMA_TAG(__GlintTagRender2D, __RENDER2D_INCX |
  313. __RENDER2D_INCY |
  314. __RENDER2D_OP_SYNCBITMASK |
  315. __RENDER2D_WIDTH(cxGlyphAligned) |
  316. __RENDER2D_HEIGHT(0));
  317. QUEUE_PXRX_DMA_TAG(__GlintTagCount, cyGlyph);
  318. QUEUE_PXRX_DMA_TAG(__GlintTagRender, __RENDER_TRAPEZOID_PRIMITIVE |
  319. __RENDER_SYNC_ON_BIT_MASK);
  320. pulGlyph = (ULONG *)pgb->aj;
  321. cjGlyph = (cxGlyphAligned >> 3) * cyGlyph;
  322. culGlyph = cjGlyph >> 2;
  323. cjGlyphRem = cjGlyph & 3;
  324. ul = culGlyph + (cjGlyphRem != 0);
  325. WAIT_PXRX_DMA_DWORDS(ul + 1);
  326. QUEUE_PXRX_DMA_HOLD(__GlintTagBitMaskPattern, ul);
  327. for ( ; --culGlyph >= 0; ++pulGlyph)
  328. {
  329. QUEUE_PXRX_DMA_DWORD(*pulGlyph);
  330. }
  331. if (cjGlyphRem)
  332. {
  333. for (ul = cj = 0; cj < cjGlyphRem; ++cj, ++(BYTE *)pulGlyph)
  334. {
  335. ul |= ((ULONG)(*(BYTE *)pulGlyph)) << (cj << 3);
  336. }
  337. QUEUE_PXRX_DMA_DWORD(ul);
  338. }
  339. }
  340. }
  341. }
  342. } while(bMore);
  343. // reset clipping
  344. if (invalidatedScissor)
  345. {
  346. glintInfo->config2D |= __CONFIG2D_USERSCISSOR;
  347. WAIT_PXRX_DMA_TAGS(1);
  348. QUEUE_PXRX_DMA_TAG(__GlintTagScissorMaxXY, 0x7FFF7FFF);
  349. }
  350. // The rasterizer's set-up to expect a continue after each Render command (NB. but not Render2D, etc),
  351. // so it won't flush the text to the framebuffer unless we specifically tell it to
  352. WAIT_PXRX_DMA_TAGS(1);
  353. QUEUE_PXRX_DMA_TAG(__GlintTagContinueNewSub, 0);
  354. DISPDBG((7, "bPxRxUncachedClippedText: exited"));
  355. return(TRUE);
  356. }
  357. //*********************************************************************************************
  358. // FUNC: DrvTextOut
  359. // ARGS: pso (I) - pointer to surface object to render to
  360. // pstro (I) - pointer to the string object to be rendered
  361. // pfo (I) - pointer to the font object
  362. // pco (I) - pointer to the clip region object
  363. // prclExtra (I) - If we had set GCAPS_HORIZSTRIKE, we would have to fill these extra
  364. // rectangles (it is used largely for underlines). It's not a big
  365. // performance win (GDI will call our DrvBitBlt to draw these).
  366. // prclOpaque (I) - pointer to the opaque background rectangle
  367. // pboFore (I) - pointer to the foreground brush object
  368. // pboOpaque (I) - pointer to the brush for the opaque background rectangle
  369. // pptlBrush (I) - pointer to the brush origin, Always unused, unless
  370. // GCAPS_ARBRUSHOPAQUE set
  371. // mix (I) - should always be a COPY operation
  372. // RETN: TRUE - pstro glyphs have been rendered
  373. //---------------------------------------------------------------------------------------------
  374. // GDI calls this function when it has strings it wants us to render: this function should be
  375. // exported in 'enable.c'.
  376. //*********************************************************************************************
  377. BOOL DrvTextOut(SURFOBJ* pso, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclExtra,
  378. RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque, POINTL* pptlBrush,
  379. MIX mix)
  380. {
  381. PDEV* ppdev;
  382. LONG xOff;
  383. DSURF* pdsurf;
  384. OH* poh;
  385. ULONG renderBits;
  386. ULONG ulColor;
  387. ULONG cGlyph;
  388. BOOL bMoreGlyphs;
  389. GLYPHPOS* pgp;
  390. BYTE iDComplexity;
  391. RECTL rclOpaque;
  392. BOOL bRet = TRUE;
  393. GLINT_DECL_VARS;
  394. pdsurf = (DSURF*) pso->dhsurf;
  395. ppdev = (PDEV*) pso->dhpdev;
  396. REMOVE_SWPOINTER(pso);
  397. if (pdsurf->dt & DT_SCREEN)
  398. {
  399. GLINT_DECL_INIT;
  400. SETUP_PPDEV_OFFSETS(ppdev, pdsurf);
  401. xOff = ppdev->xOffset = pdsurf->poh->x;
  402. VALIDATE_DD_CONTEXT;
  403. DISPDBG((9, "DrvTextOut: ppdev = %p pso->dhsurf->dt == %d", ppdev, pdsurf->dt));
  404. // The DDI spec says we'll only ever get foreground and background mixes of R2_COPYPEN:
  405. ASSERTDD(mix == 0x0d0d, "GDI should only give us a copy mix");
  406. if (glintInfo->WriteMask != 0xffffffff)
  407. {
  408. // the texture unit requires all 32bpp of pixel data, so if we've got the upper
  409. // 8 bits masked out for overlays we need to reenable these bits temporarily
  410. WAIT_PXRX_DMA_TAGS(1);
  411. glintInfo->WriteMask = 0xffffffff;
  412. QUEUE_PXRX_DMA_TAG(__GlintTagFBHardwareWriteMask, 0xffffffff);
  413. }
  414. iDComplexity = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
  415. if (prclOpaque != NULL)
  416. {
  417. int x,y,cx,cy;
  418. RBRUSH_COLOR rbc;
  419. ////////////////////////////////////////////////////////////
  420. // Opaque Initialization
  421. ////////////////////////////////////////////////////////////
  422. if (iDComplexity == DC_TRIVIAL)
  423. {
  424. DrawOpaqueRect:
  425. rbc.iSolidColor = pboOpaque->iSolidColor;
  426. ppdev->pgfnFillSolid(ppdev, 1, prclOpaque, __GLINT_LOGICOP_COPY, __GLINT_LOGICOP_COPY, rbc, NULL);
  427. }
  428. else if (iDComplexity == DC_RECT)
  429. {
  430. DISPDBG((7, "DrvTextOut: drawing opaquing rect with rectangular clipping"));
  431. if (bIntersect(prclOpaque, &pco->rclBounds, &rclOpaque))
  432. {
  433. prclOpaque = &rclOpaque;
  434. goto DrawOpaqueRect;
  435. }
  436. }
  437. else
  438. {
  439. // vPxRxClipSolid modifies the rect list we pass in but prclOpaque
  440. // is probably a GDI structure so don't change it. This is also
  441. // necessary for multi-headed drivers.
  442. RECTL tmpOpaque = *prclOpaque;
  443. DISPDBG((7, "DrvTextOut: drawing opaquing rect with complex clipping"));
  444. vPxRxClipSolid(ppdev, 1, &tmpOpaque, pboOpaque->iSolidColor, pco);
  445. }
  446. }
  447. if (prclOpaque == NULL)
  448. {
  449. // opaque initialization would have ensured the registers were correctly
  450. // set up for a solid fill, without it we'll need to perform our own
  451. // initialization.
  452. SET_WRITE_BUFFERS;
  453. WAIT_PXRX_DMA_TAGS(1);
  454. LOAD_CONFIG2D(__CONFIG2D_CONSTANTSRC | __CONFIG2D_FBWRITE);
  455. }
  456. ////////////////////////////////////////////////////////////
  457. // Transparent Initialization
  458. ////////////////////////////////////////////////////////////
  459. ulColor = pboFore->iSolidColor;
  460. WAIT_PXRX_DMA_TAGS(1);
  461. LOAD_FOREGROUNDCOLOUR( ulColor );
  462. STROBJ_vEnumStart(pstro);
  463. do {
  464. if (pstro->pgp != NULL)
  465. {
  466. // There's only the one batch of glyphs, so save ourselves a call:
  467. pgp = pstro->pgp;
  468. cGlyph = pstro->cGlyphs;
  469. bMoreGlyphs = FALSE;
  470. }
  471. else
  472. {
  473. // never get here in WinBench97 business graphics
  474. bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyph, &pgp);
  475. }
  476. if (cGlyph > 0)
  477. {
  478. // fall back to uncached rendering
  479. if (iDComplexity == DC_TRIVIAL)
  480. {
  481. bRet = bPxRxUncachedText(ppdev, pgp, cGlyph, pstro->ulCharInc);
  482. }
  483. else
  484. {
  485. bRet = bPxRxUncachedClippedText(ppdev, pgp, cGlyph, pstro->ulCharInc, pco);
  486. }
  487. }
  488. } while (bMoreGlyphs && bRet);
  489. if(glintInfo->DefaultWriteMask != 0xffffffff)
  490. {
  491. WAIT_PXRX_DMA_TAGS(1);
  492. glintInfo->WriteMask = glintInfo->DefaultWriteMask;
  493. QUEUE_PXRX_DMA_TAG(__GlintTagFBHardwareWriteMask, glintInfo->DefaultWriteMask);
  494. }
  495. SEND_PXRX_DMA_QUERY;
  496. }
  497. else
  498. {
  499. // We're drawing to a DFB we've converted to a DIB, so just call GDI
  500. // to handle it:
  501. return(EngTextOut(pdsurf->pso, pstro, pfo, pco, prclExtra, prclOpaque,
  502. pboFore, pboOpaque, pptlBrush, mix));
  503. }
  504. DISPDBG((9, "DrvTextOut: exiting"));
  505. return(bRet);
  506. }