Source code of Windows XP (NT5)
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.

667 lines
22 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: textout.c
  3. *
  4. * There are three basic methods for drawing text with hardware
  5. * acceleration:
  6. *
  7. * 1) Glyph caching -- Glyph bitmaps are cached by the accelerator
  8. * (probably in off-screen memory), and text is drawn by
  9. * referring the hardware to the cached glyph locations.
  10. *
  11. * 2) Glyph expansion -- Each individual glyph is colour-expanded
  12. * directly to the screen from the monochrome glyph bitmap
  13. * supplied by GDI.
  14. *
  15. * 3) Buffer expansion -- The CPU is used to draw all the glyphs into
  16. * a 1bpp monochrome bitmap, and the hardware is then used
  17. * to colour-expand the result.
  18. *
  19. * The fastest method depends on a number of variables, such as the
  20. * colour expansion speed, bus speed, CPU speed, average glyph size,
  21. * and average string length.
  22. *
  23. * For the S3 with normal sized glyphs, I've found that caching the
  24. * glyphs in off-screen memory is typically the slowest method.
  25. * Buffer expansion is typically fastest on the slow ISA bus (or when
  26. * memory-mapped I/O isn't available on the x86), and glyph expansion
  27. * is best on fast buses such as VL and PCI.
  28. *
  29. * Glyph expansion is typically faster than buffer expansion for very
  30. * large glyphs, even on the ISA bus, because less copying by the CPU
  31. * needs to be done. Unfortunately, large glyphs are pretty rare.
  32. *
  33. * An advantange of the buffer expansion method is that opaque text will
  34. * never flash -- the other two methods typically need to draw the
  35. * opaquing rectangle before laying down the glyphs, which may cause
  36. * a flash if the raster is caught at the wrong time.
  37. *
  38. * This driver implements glyph expansion and buffer expansion --
  39. * methods 2) and 3). Depending on the hardware capabilities at
  40. * run-time, we'll use whichever one will be faster.
  41. *
  42. * Copyright (c) 1992-1994 Microsoft Corporation
  43. *
  44. \**************************************************************************/
  45. #include "precomp.h"
  46. POINTL gptlZero = { 0, 0 }; // Specifies that the origin of the
  47. // temporary buffer given to the 1bpp
  48. // transfer routine for fasttext is
  49. // at (0, 0)
  50. #define FIFTEEN_BITS ((1 << 15)-1)
  51. /******************************Public*Routine******************************\
  52. * VOID vClipSolid
  53. *
  54. * Fills the specified rectangles with the specified colour, honouring
  55. * the requested clipping. No more than four rectangles should be passed in.
  56. * Intended for drawing the areas of the opaquing rectangle that extend
  57. * beyond the text box. The rectangles must be in left to right, top to
  58. * bottom order. Assumes there is at least one rectangle in the list.
  59. *
  60. \**************************************************************************/
  61. VOID vClipSolid(
  62. PDEV* ppdev,
  63. LONG crcl,
  64. RECTL* prcl,
  65. ULONG iColor,
  66. CLIPOBJ* pco)
  67. {
  68. BOOL bMore; // Flag for clip enumeration
  69. CLIPENUM ce; // Clip enumeration object
  70. ULONG i;
  71. ULONG j;
  72. RECTL arclTmp[4];
  73. ULONG crclTmp;
  74. RECTL* prclTmp;
  75. RECTL* prclClipTmp;
  76. LONG iLastBottom;
  77. RECTL* prclClip;
  78. RBRUSH_COLOR rbc;
  79. ASSERTDD((crcl > 0) && (crcl <= 4), "Expected 1 to 4 rectangles");
  80. ASSERTDD((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL),
  81. "Expected a non-null clip object");
  82. rbc.iSolidColor = iColor;
  83. if (pco->iDComplexity == DC_RECT)
  84. {
  85. crcl = cIntersect(&pco->rclBounds, prcl, crcl);
  86. if (crcl != 0)
  87. {
  88. (ppdev->pfnFillSolid)(ppdev, crcl, prcl, OVERPAINT, OVERPAINT,
  89. rbc, NULL);
  90. }
  91. }
  92. else // iDComplexity == DC_COMPLEX
  93. {
  94. // Bottom of last rectangle to fill
  95. iLastBottom = prcl[crcl - 1].bottom;
  96. // Initialize the clip rectangle enumeration to right-down so we can
  97. // take advantage of the rectangle list being right-down:
  98. CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_RIGHTDOWN, 0);
  99. // Scan through all the clip rectangles, looking for intersects
  100. // of fill areas with region rectangles:
  101. do {
  102. // Get a batch of region rectangles:
  103. bMore = CLIPOBJ_bEnum(pco, sizeof(ce), (VOID*)&ce);
  104. // Clip the rect list to each region rect:
  105. for (j = ce.c, prclClip = ce.arcl; j-- > 0; prclClip++)
  106. {
  107. // Since the rectangles and the region enumeration are both
  108. // right-down, we can zip through the region until we reach
  109. // the first fill rect, and are done when we've passed the
  110. // last fill rect.
  111. if (prclClip->top >= iLastBottom)
  112. {
  113. // Past last fill rectangle; nothing left to do:
  114. return;
  115. }
  116. // Do intersection tests only if we've reached the top of
  117. // the first rectangle to fill:
  118. if (prclClip->bottom > prcl->top)
  119. {
  120. // We've reached the top Y scan of the first rect, so
  121. // it's worth bothering checking for intersection.
  122. // Generate a list of the rects clipped to this region
  123. // rect:
  124. prclTmp = prcl;
  125. prclClipTmp = arclTmp;
  126. for (i = crcl, crclTmp = 0; i-- != 0; prclTmp++)
  127. {
  128. // Intersect fill and clip rectangles
  129. if (bIntersect(prclTmp, prclClip, prclClipTmp))
  130. {
  131. // Add to list if anything's left to draw:
  132. crclTmp++;
  133. prclClipTmp++;
  134. }
  135. }
  136. // Draw the clipped rects
  137. if (crclTmp != 0)
  138. {
  139. (ppdev->pfnFillSolid)(ppdev, crclTmp, &arclTmp[0],
  140. OVERPAINT, OVERPAINT, rbc, NULL);
  141. }
  142. }
  143. }
  144. } while (bMore);
  145. }
  146. }
  147. /******************************Public*Routine******************************\
  148. * BOOL bBufferExpansion
  149. *
  150. * Outputs text using the 'buffer expansion' method. The CPU draws to a
  151. * 1bpp buffer, and the result is colour-expanded to the screen using the
  152. * hardware.
  153. *
  154. * Note that this is x86 only ('vFastText', which draws the glyphs to the
  155. * 1bpp buffer, is writen in Asm).
  156. *
  157. * If you're just getting your driver working, this is the fastest way to
  158. * bring up working accelerated text. All you have to do is write the
  159. * 'Xfer1bpp' function that's also used by the blt code. This
  160. * 'bBufferExpansion' routine shouldn't need to be modified at all.
  161. *
  162. \**************************************************************************/
  163. #if defined(i386)
  164. BOOL bBufferExpansion(
  165. PDEV* ppdev,
  166. STROBJ* pstro,
  167. CLIPOBJ* pco,
  168. RECTL* prclExtra,
  169. RECTL* prclOpaque,
  170. BRUSHOBJ* pboFore,
  171. BRUSHOBJ* pboOpaque)
  172. {
  173. BYTE jClip;
  174. BOOL bMore; // Flag for clip enumeration
  175. GLYPHPOS* pgp; // Points to the first glyph
  176. BOOL bMoreGlyphs; // Glyph enumeration flag
  177. ULONG cGlyph; // # of glyphs in one batch
  178. RECTL arclTmp[4]; // Temporary storage for portions
  179. // of opaquing rectangle
  180. RECTL* prclClip; // Points to list of clip rectangles
  181. RECTL* prclDraw; // Actual text to be drawn
  182. RECTL rclDraw;
  183. ULONG crcl; // Temporary rectangle count
  184. ULONG ulBufferBytes;
  185. ULONG ulBufferHeight;
  186. BOOL bTextPerfectFit;
  187. ULONG flDraw;
  188. BOOL bTmpAlloc;
  189. SURFOBJ so;
  190. CLIPENUM ce;
  191. RBRUSH_COLOR rbc;
  192. ULONG ulHwBackMix; // Dictates whether opaque or
  193. // transparent text
  194. XLATEOBJ xlo; // Temporary for passing colours
  195. XLATECOLORS xlc; // Temporary for keeping colours
  196. jClip = (pco == NULL) ? DC_TRIVIAL : pco->iDComplexity;
  197. // The foreground colour will always be solid:
  198. xlc.iForeColor = pboFore->iSolidColor;
  199. ASSERTDD(xlc.iForeColor != -1, "Expected solid foreground colour");
  200. // See if the temporary buffer is big enough for the text; if
  201. // not, try to allocate enough memory. We round up to the
  202. // nearest dword multiple:
  203. so.lDelta = ((((pstro->rclBkGround.right + 31) & ~31) -
  204. (pstro->rclBkGround.left & ~31)) >> 3);
  205. ulBufferHeight = pstro->rclBkGround.bottom - pstro->rclBkGround.top;
  206. ulBufferBytes = so.lDelta * ulBufferHeight;
  207. if (((ULONG)so.lDelta > FIFTEEN_BITS) ||
  208. (ulBufferHeight > FIFTEEN_BITS))
  209. {
  210. // the math will have overflowed
  211. return(FALSE);
  212. }
  213. // Use our temporary buffer if it's big enough, otherwise
  214. // allocate a buffer on the fly:
  215. if (ulBufferBytes >= TMP_BUFFER_SIZE)
  216. {
  217. // The textout is so big that I doubt this allocation will
  218. // cost a significant amount in performance:
  219. bTmpAlloc = TRUE;
  220. so.pvScan0 = EngAllocUserMem(ulBufferBytes, ALLOC_TAG);
  221. if (so.pvScan0 == NULL)
  222. return(FALSE);
  223. }
  224. else
  225. {
  226. bTmpAlloc = FALSE;
  227. so.pvScan0 = ppdev->pvTmpBuffer;
  228. }
  229. // Set fixed pitch, overlap, and top and bottom 'y' alignment
  230. // flags:
  231. if (!(pstro->flAccel & SO_HORIZONTAL) ||
  232. (pstro->flAccel & SO_REVERSED))
  233. {
  234. flDraw = 0;
  235. }
  236. else
  237. {
  238. flDraw = ((pstro->ulCharInc != 0) ? 0x01 : 0) |
  239. (((pstro->flAccel & (SO_ZERO_BEARINGS |
  240. SO_FLAG_DEFAULT_PLACEMENT)) !=
  241. (SO_ZERO_BEARINGS | SO_FLAG_DEFAULT_PLACEMENT))
  242. ? 0x02 : 0) |
  243. (((pstro->flAccel & (SO_ZERO_BEARINGS |
  244. SO_FLAG_DEFAULT_PLACEMENT |
  245. SO_MAXEXT_EQUAL_BM_SIDE)) ==
  246. (SO_ZERO_BEARINGS | SO_FLAG_DEFAULT_PLACEMENT |
  247. SO_MAXEXT_EQUAL_BM_SIDE)) ? 0x04 : 0);
  248. }
  249. // If there's an opaque rectangle, we'll do as much opaquing
  250. // as possible as we do the text. If the opaque rectangle is
  251. // larger than the text rectangle, then we'll do the fringe
  252. // areas right now, and the text and associated background
  253. // areas together later:
  254. ulHwBackMix = LEAVE_ALONE;
  255. if (prclOpaque != NULL)
  256. {
  257. ulHwBackMix = OVERPAINT;
  258. // Since we didn't set GCAPS_ARBRUSHOPAQUE (yes, it's
  259. // missing a 'b'), we don't have to worry about getting
  260. // anything other that a solid opaquing brush. I wouldn't
  261. // recommend handling it anyway, since I'll bet it would
  262. // break quite a few applications:
  263. xlc.iBackColor = pboOpaque->iSolidColor;
  264. ASSERTDD(xlc.iBackColor != -1, "Expected solid background colour");
  265. // See if we have fringe areas to do. If so, build a list of
  266. // rectangles to fill, in right-down order:
  267. crcl = 0;
  268. // Top fragment:
  269. if (pstro->rclBkGround.top > prclOpaque->top)
  270. {
  271. arclTmp[crcl].top = prclOpaque->top;
  272. arclTmp[crcl].left = prclOpaque->left;
  273. arclTmp[crcl].right = prclOpaque->right;
  274. arclTmp[crcl++].bottom = pstro->rclBkGround.top;
  275. }
  276. // Left fragment:
  277. if (pstro->rclBkGround.left > prclOpaque->left)
  278. {
  279. arclTmp[crcl].top = pstro->rclBkGround.top;
  280. arclTmp[crcl].left = prclOpaque->left;
  281. arclTmp[crcl].right = pstro->rclBkGround.left;
  282. arclTmp[crcl++].bottom = pstro->rclBkGround.bottom;
  283. }
  284. // Right fragment:
  285. if (pstro->rclBkGround.right < prclOpaque->right)
  286. {
  287. arclTmp[crcl].top = pstro->rclBkGround.top;
  288. arclTmp[crcl].right = prclOpaque->right;
  289. arclTmp[crcl].left = pstro->rclBkGround.right;
  290. arclTmp[crcl++].bottom = pstro->rclBkGround.bottom;
  291. }
  292. // Bottom fragment:
  293. if (pstro->rclBkGround.bottom < prclOpaque->bottom)
  294. {
  295. arclTmp[crcl].bottom = prclOpaque->bottom;
  296. arclTmp[crcl].left = prclOpaque->left;
  297. arclTmp[crcl].right = prclOpaque->right;
  298. arclTmp[crcl++].top = pstro->rclBkGround.bottom;
  299. }
  300. // Fill any fringe rectangles we found:
  301. if (crcl != 0)
  302. {
  303. if (jClip == DC_TRIVIAL)
  304. {
  305. rbc.iSolidColor = xlc.iBackColor;
  306. (ppdev->pfnFillSolid)(ppdev, crcl, arclTmp, OVERPAINT,
  307. OVERPAINT, rbc, NULL);
  308. }
  309. else
  310. {
  311. vClipSolid(ppdev, crcl, arclTmp, xlc.iBackColor, pco);
  312. }
  313. }
  314. }
  315. // We're done with separate opaquing; any further opaquing will
  316. // happen as part of the text drawing.
  317. // Clear the buffer if the text isn't going to set every bit:
  318. bTextPerfectFit = (pstro->flAccel & (SO_ZERO_BEARINGS |
  319. SO_FLAG_DEFAULT_PLACEMENT | SO_MAXEXT_EQUAL_BM_SIDE |
  320. SO_CHAR_INC_EQUAL_BM_BASE)) ==
  321. (SO_ZERO_BEARINGS | SO_FLAG_DEFAULT_PLACEMENT |
  322. SO_MAXEXT_EQUAL_BM_SIDE | SO_CHAR_INC_EQUAL_BM_BASE);
  323. if (!bTextPerfectFit)
  324. {
  325. // Note that we already rounded up to a dword multiple size.
  326. vClearMemDword((ULONG*) so.pvScan0, ulBufferBytes >> 2);
  327. }
  328. // Fake up the translate object that will provide the 1bpp
  329. // transfer routine the foreground and background colours:
  330. xlo.pulXlate = (ULONG*) &xlc;
  331. // Draw the text into the temp buffer, and thence to the screen:
  332. do
  333. {
  334. // Get the next batch of glyphs:
  335. if (pstro->pgp != NULL)
  336. {
  337. // There's only the one batch of glyphs, so save ourselves
  338. // a call:
  339. pgp = pstro->pgp;
  340. cGlyph = pstro->cGlyphs;
  341. bMoreGlyphs = FALSE;
  342. }
  343. else
  344. {
  345. bMoreGlyphs = STROBJ_bEnum(pstro, &cGlyph, &pgp);
  346. }
  347. // LATER: remove double clip intersection from ASM code
  348. if (cGlyph)
  349. {
  350. prclClip = NULL;
  351. prclDraw = &pstro->rclBkGround;
  352. if (jClip == DC_TRIVIAL)
  353. {
  354. Output_Text:
  355. vFastText(pgp,
  356. cGlyph,
  357. so.pvScan0,
  358. so.lDelta,
  359. pstro->ulCharInc,
  360. &pstro->rclBkGround,
  361. prclOpaque,
  362. flDraw,
  363. prclClip,
  364. prclExtra);
  365. if (!bMoreGlyphs)
  366. {
  367. (ppdev->pfnXfer1bpp)(ppdev,
  368. 1,
  369. prclDraw,
  370. OVERPAINT,
  371. ulHwBackMix,
  372. &so,
  373. &gptlZero,
  374. &pstro->rclBkGround,
  375. &xlo);
  376. }
  377. }
  378. else if (jClip == DC_RECT)
  379. {
  380. if (bIntersect(&pco->rclBounds, &pstro->rclBkGround,
  381. &rclDraw))
  382. {
  383. arclTmp[0] = pco->rclBounds;
  384. arclTmp[1].bottom = 0; // Terminate list
  385. prclClip = &arclTmp[0];
  386. prclDraw = &rclDraw;
  387. // Save some code size by jumping to the common
  388. // functions calls:
  389. goto Output_Text;
  390. }
  391. }
  392. else // jClip == DC_COMPLEX
  393. {
  394. CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES,
  395. CD_ANY, 0);
  396. do
  397. {
  398. bMore = CLIPOBJ_bEnum(pco,
  399. sizeof(ce) - sizeof(RECTL),
  400. (ULONG*) &ce);
  401. ce.c = cIntersect(&pstro->rclBkGround,
  402. ce.arcl, ce.c);
  403. if (ce.c != 0)
  404. {
  405. ce.arcl[ce.c].bottom = 0; // Terminate list
  406. vFastText(pgp,
  407. cGlyph,
  408. so.pvScan0,
  409. so.lDelta,
  410. pstro->ulCharInc,
  411. &pstro->rclBkGround,
  412. prclOpaque,
  413. flDraw,
  414. &ce.arcl[0],
  415. prclExtra);
  416. if (!bMoreGlyphs)
  417. {
  418. (ppdev->pfnXfer1bpp)(ppdev,
  419. ce.c,
  420. &ce.arcl[0],
  421. OVERPAINT,
  422. ulHwBackMix,
  423. &so,
  424. &gptlZero,
  425. &pstro->rclBkGround,
  426. &xlo);
  427. }
  428. }
  429. } while (bMore);
  430. break;
  431. }
  432. }
  433. } while (bMoreGlyphs);
  434. // Free up any memory we allocated for the temp buffer:
  435. if (bTmpAlloc)
  436. {
  437. EngFreeUserMem(so.pvScan0);
  438. }
  439. return(TRUE);
  440. }
  441. #endif // defined(i386)
  442. /******************************Public*Routine******************************\
  443. * BOOL DrvTextOut
  444. *
  445. * If it's the fastest method, outputs text using the 'glyph expansion'
  446. * method. Each individual glyph is colour-expanded directly to the
  447. * screen from the monochrome glyph bitmap supplied by GDI.
  448. *
  449. * If it's not the fastest method, calls the routine that implements the
  450. * 'buffer expansion' method.
  451. *
  452. \**************************************************************************/
  453. BOOL DrvTextOut(
  454. SURFOBJ* pso,
  455. STROBJ* pstro,
  456. FONTOBJ* pfo,
  457. CLIPOBJ* pco,
  458. RECTL* prclExtra, // If we had set GCAPS_HORIZSTRIKE, we would have
  459. // to fill these extra rectangles (it is used
  460. // largely for underlines). It's not a big
  461. // performance win (GDI will call our DrvBitBlt
  462. // to draw the extra rectangles).
  463. RECTL* prclOpaque,
  464. BRUSHOBJ* pboFore,
  465. BRUSHOBJ* pboOpaque,
  466. POINTL* pptlBrush,
  467. MIX mix)
  468. {
  469. PDEV* ppdev;
  470. DSURF* pdsurf;
  471. OH* poh;
  472. // The DDI spec says we'll only ever get foreground and background
  473. // mixes of R2_COPYPEN:
  474. ASSERTDD(mix == 0x0d0d, "GDI should only give us a copy mix");
  475. // Pass the surface off to GDI if it's a device bitmap that we've
  476. // converted to a DIB:
  477. pdsurf = (DSURF*) pso->dhsurf;
  478. if (pdsurf->dt != DT_DIB)
  479. {
  480. // We'll be drawing to the screen or an off-screen DFB; copy the
  481. // surface's offset now so that we won't need to refer to the DSURF
  482. // again:
  483. poh = pdsurf->poh;
  484. ppdev = (PDEV*) pso->dhpdev;
  485. ppdev->xOffset = poh->x;
  486. ppdev->yOffset = poh->y;
  487. // We don't want to use the 'glyph expansion' method, so use
  488. // the 'buffer expansion' method instead:
  489. return(bBufferExpansion(ppdev, pstro, pco, prclExtra, prclOpaque,
  490. pboFore, pboOpaque));
  491. }
  492. else
  493. {
  494. // We're drawing to a DFB we've converted to a DIB, so just call GDI
  495. // to handle it:
  496. return(EngTextOut(pdsurf->pso, pstro, pfo, pco, prclExtra, prclOpaque,
  497. pboFore, pboOpaque, pptlBrush, mix));
  498. }
  499. return(TRUE);
  500. }
  501. /******************************Public*Routine******************************\
  502. * BOOL bEnableText
  503. *
  504. * Performs the necessary setup for the text drawing subcomponent.
  505. *
  506. \**************************************************************************/
  507. BOOL bEnableText(
  508. PDEV* ppdev)
  509. {
  510. // Our text algorithms require no initialization. If we were to
  511. // do glyph caching, we would probably want to allocate off-screen
  512. // memory and do a bunch of other stuff here.
  513. return(TRUE);
  514. }
  515. /******************************Public*Routine******************************\
  516. * VOID vDisableText
  517. *
  518. * Performs the necessary clean-up for the text drawing subcomponent.
  519. *
  520. \**************************************************************************/
  521. VOID vDisableText(PDEV* ppdev)
  522. {
  523. // Here we free any stuff allocated in 'bEnableText'.
  524. }
  525. /******************************Public*Routine******************************\
  526. * VOID vAssertModeText
  527. *
  528. * Disables or re-enables the text drawing subcomponent in preparation for
  529. * full-screen entry/exit.
  530. *
  531. \**************************************************************************/
  532. VOID vAssertModeText(
  533. PDEV* ppdev,
  534. BOOL bEnable)
  535. {
  536. // If we were to do off-screen glyph caching, we would probably want
  537. // to invalidate our cache here, because it will get destroyed when
  538. // we switch to full-screen.
  539. }
  540. /******************************Public*Routine******************************\
  541. * VOID DrvDestroyFont
  542. *
  543. * We're being notified that the given font is being deallocated; clean up
  544. * anything we've stashed in the 'pvConsumer' field of the 'pfo'.
  545. *
  546. \**************************************************************************/
  547. VOID DrvDestroyFont(FONTOBJ *pfo)
  548. {
  549. // This call isn't hooked, so GDI will never call it.
  550. //
  551. // This merely exists as a stub function for the sample multi-screen
  552. // support, so that MulDestroyFont can illustrate how multiple screen
  553. // text supports when the driver caches glyphs. If this driver did
  554. // glyph caching, we might have used the 'pvConsumer' field of the
  555. // 'pfo', which we would have to clean up.
  556. }