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.

950 lines
33 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: fillpath.c
  3. *
  4. * DrvFillPath
  5. *
  6. * Copyright (c) 1992-1993 Microsoft Corporation
  7. \**************************************************************************/
  8. // LATER identify convex polygons and special-case?
  9. // LATER identify vertical edges and special-case?
  10. // LATER move pointed-to variables into automatics in search loops
  11. // LATER punt to the engine with segmented framebuffer callbacks
  12. // LATER handle complex clipping
  13. // LATER coalesce rectangles
  14. #include "driver.h"
  15. #include "limits.h"
  16. #define TAKING_ALLOC_STATS 0
  17. #if TAKING_ALLOC_STATS
  18. ULONG BufferHitInFillpath = 0;
  19. ULONG BufferMissInFillpath = 0;
  20. #endif
  21. // Describe a single non-horizontal edge of a path to fill.
  22. typedef struct _EDGE {
  23. PVOID pNext;
  24. INT iScansLeft;
  25. INT X;
  26. INT Y;
  27. INT iErrorTerm;
  28. INT iErrorAdjustUp;
  29. INT iErrorAdjustDown;
  30. INT iXWhole;
  31. INT iXDirection;
  32. INT iWindingDirection;
  33. } EDGE, *PEDGE;
  34. // Maximum number of rects we'll fill per call to
  35. // the fill code
  36. #define MAX_PATH_RECTS 50
  37. #define RECT_BYTES (MAX_PATH_RECTS * sizeof(RECT))
  38. #define EDGE_BYTES (GLOBAL_BUFFER_SIZE - RECT_BYTES)
  39. #define MAX_EDGES (EDGE_BYTES/sizeof(EDGE))
  40. //MIX translation table. Translates a mix 1-16, into an old style Rop 0-255.
  41. extern BYTE gaMix[];
  42. VOID AdvanceAETEdges(EDGE *pAETHead);
  43. VOID XSortAETEdges(EDGE *pAETHead);
  44. VOID MoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, INT iCurrentY);
  45. EDGE * AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge, POINTFIX *ppfxEdgeStart,
  46. POINTFIX *ppfxEdgeEnd, RECTL *pClipRect);
  47. BOOL ConstructGET(EDGE *pGETHead, EDGE *pFreeEdges, PATHOBJ *ppo,
  48. PATHDATA *pd, BOOL bMore, RECTL *pClipRect);
  49. void AdjustErrorTerm(INT *pErrorTerm, INT iErrorAdjustUp, INT iErrorAdjustDown,
  50. INT yJump, INT *pXStart, INT iXDirection);
  51. /******************************Public*Routine******************************\
  52. * DrvFillPath
  53. *
  54. * Fill the specified path with the specified brush and ROP.
  55. *
  56. \**************************************************************************/
  57. BOOL DrvFillPath
  58. (
  59. SURFOBJ *pso,
  60. PATHOBJ *ppo,
  61. CLIPOBJ *pco,
  62. BRUSHOBJ *pbo,
  63. POINTL *pptlBrush,
  64. MIX mix,
  65. FLONG flOptions
  66. )
  67. {
  68. PPDEV ppdev;
  69. BYTE jClipping; // clipping type
  70. EDGE *pCurrentEdge;
  71. EDGE AETHead; // dummy head/tail node & sentinel for Active Edge Table
  72. EDGE *pAETHead; // pointer to AETHead
  73. EDGE GETHead; // dummy head/tail node & sentinel for Global Edge Table
  74. EDGE *pGETHead; // pointer to GETHead
  75. EDGE *pFreeEdges; // pointer to memory free for use to store edges
  76. ULONG ulNumRects; // # of rectangles to draw currently in rectangle list
  77. RECTL *prclRects; // pointer to start of rectangle draw list
  78. INT iCurrentY; // scan line for which we're currently scanning out the
  79. // fill
  80. RBRUSH_COLOR rbc; // Realized brush or solid color
  81. PFNFILL pfnFill;// Points to appropriate fill routine
  82. BOOL bMore;
  83. PATHDATA pd;
  84. RECTL ClipRect;
  85. RECTL *pRectBuf;
  86. EDGE *pEdgeBuf;
  87. BOOL bRetVal=FALSE; //FALSE until proven TRUE
  88. BOOL bMemAlloced=FALSE; //FALSE until proven TRUE
  89. // The drawing surface
  90. ppdev = (PPDEV) pso->dhsurf;
  91. pRectBuf = (RECTL*) ((BYTE *)ppdev->pvTmpBuf);
  92. pEdgeBuf = (EDGE*) ((BYTE *)ppdev->pvTmpBuf + RECT_BYTES);
  93. // Our rectangle fill routines work only in planar mode:
  94. if (!(ppdev->fl & DRIVER_PLANAR_CAPABLE))
  95. goto ReturnFalse;
  96. // Set up the clipping
  97. if (pco == (CLIPOBJ *) NULL) {
  98. // No CLIPOBJ provided, so we don't have to worry about clipping
  99. jClipping = DC_TRIVIAL;
  100. } else {
  101. // Use the CLIPOBJ-provided clipping
  102. jClipping = pco->iDComplexity;
  103. }
  104. if (jClipping != DC_TRIVIAL) {
  105. if (jClipping != DC_RECT) {
  106. goto ReturnFalse; // there is complex clipping; let GDI fill the path
  107. }
  108. // Clip to the clip rectangle
  109. ClipRect = pco->rclBounds;
  110. } else {
  111. // So the y-clipping code doesn't do any clipping
  112. // /16 so we don't blow the values out when we scale up to GIQ
  113. ClipRect.top = (LONG_MIN + 1) / 16; // +1 to avoid compiler problem
  114. ClipRect.bottom = LONG_MAX / 16;
  115. }
  116. // There's nothing to do if there are only one or two points
  117. if (ppo->cCurves <= 2) {
  118. goto ReturnTrue;
  119. }
  120. // See if we can handle this pattern and ROP
  121. // We don't handle ROP4s
  122. if ((mix & 0xFF) != ((mix >> 8) & 0xFF)) {
  123. goto ReturnFalse; // it's a ROP4; let GDI fill the path
  124. }
  125. // See if we can use the solid brush accelerators, or have to draw a
  126. // pattern
  127. switch (mix &= 0xFF)
  128. {
  129. case R2_MASKNOTPEN:
  130. case R2_NOTCOPYPEN:
  131. case R2_XORPEN:
  132. case R2_MASKPEN:
  133. case R2_NOTXORPEN:
  134. case R2_MERGENOTPEN:
  135. case R2_COPYPEN:
  136. case R2_MERGEPEN:
  137. case R2_NOTMERGEPEN:
  138. case R2_MASKPENNOT:
  139. case R2_NOTMASKPEN:
  140. case R2_MERGEPENNOT:
  141. // vTrgBlt can only handle solid color fills
  142. if (pbo->iSolidColor != 0xffffffff)
  143. {
  144. rbc.iSolidColor = pbo->iSolidColor;
  145. pfnFill = vTrgBlt;
  146. }
  147. else
  148. {
  149. rbc.prb = (RBRUSH*) pbo->pvRbrush;
  150. if (rbc.prb == NULL)
  151. {
  152. rbc.prb = (RBRUSH*) BRUSHOBJ_pvGetRbrush(pbo);
  153. if (rbc.prb == NULL)
  154. {
  155. // If we haven't realized the brush, punt the call:
  156. goto ReturnFalse;
  157. }
  158. }
  159. if (!(rbc.prb->fl & RBRUSH_BLACKWHITE) &&
  160. ((mix & 0xff) != R2_COPYPEN))
  161. {
  162. // Only black/white brushes can handle ROPs other
  163. // than COPYPEN:
  164. goto ReturnFalse;
  165. }
  166. if (rbc.prb->fl & RBRUSH_NCOLOR)
  167. pfnFill = vColorPat;
  168. else
  169. pfnFill = vMonoPat;
  170. }
  171. break;
  172. // Rops that are implicit solid colors
  173. case R2_NOT:
  174. case R2_WHITE:
  175. case R2_BLACK:
  176. // Brush color parameter doesn't matter for these rops
  177. pfnFill = vTrgBlt;
  178. break;
  179. case R2_NOP:
  180. goto ReturnTrue;
  181. default:
  182. RIP("DrvFillPath: bad mix == 0");
  183. }
  184. // Set up working storage in the temporary buffer
  185. prclRects = pRectBuf; // storage for list of rectangles to draw
  186. // Enumerate path here first time to check for special
  187. // cases (rectangles and monotone polygons)
  188. // It is too difficult to determine interaction between
  189. // multiple paths, if there is more than one, skip this
  190. if (!(bMore = PATHOBJ_bEnum(ppo, &pd))) {
  191. RECTL *rectangle;
  192. INT cPoints = pd.count;
  193. // The count can't be less than three, because we got all the edges
  194. // in this subpath, and above we checked that there were at least
  195. // three edges
  196. // If the count is four, check to see if the polygon is really a
  197. // rectangle since we can really speed that up. We'll also check for
  198. // five with the first and last points the same, because under Win 3.1,
  199. // it was required to close polygons
  200. if ((cPoints == 4) ||
  201. ((cPoints == 5) &&
  202. (pd.pptfx[0].x == pd.pptfx[4].x) &&
  203. (pd.pptfx[0].y == pd.pptfx[4].y))) {
  204. rectangle = prclRects;
  205. /* we have to start somewhere so assume that most
  206. applications specify the top left point first
  207. we want to check that the first two points are
  208. either vertically or horizontally aligned. if
  209. they are then we check that the last point [3]
  210. is either horizontally or vertically aligned,
  211. and finally that the 3rd point [2] is aligned
  212. with both the first point and the last point */
  213. #define FIX_SHIFT 4L
  214. #define FIX_MASK (- (1 << FIX_SHIFT))
  215. rectangle->top = pd.pptfx[0].y - 1 & FIX_MASK;
  216. rectangle->left = pd.pptfx[0].x - 1 & FIX_MASK;
  217. rectangle->right = pd.pptfx[1].x - 1 & FIX_MASK;
  218. if (rectangle->left ^ rectangle->right) {
  219. if (rectangle->top ^ (pd.pptfx[1].y - 1 & FIX_MASK))
  220. goto not_rectangle;
  221. if (rectangle->left ^ (pd.pptfx[3].x - 1 & FIX_MASK))
  222. goto not_rectangle;
  223. if (rectangle->right ^ (pd.pptfx[2].x - 1 & FIX_MASK))
  224. goto not_rectangle;
  225. rectangle->bottom = pd.pptfx[2].y - 1 & FIX_MASK;
  226. if (rectangle->bottom ^ (pd.pptfx[3].y - 1 & FIX_MASK))
  227. goto not_rectangle;
  228. }
  229. else {
  230. if (rectangle->top ^ (pd.pptfx[3].y - 1 & FIX_MASK))
  231. goto not_rectangle;
  232. rectangle->bottom = pd.pptfx[1].y - 1 & FIX_MASK;
  233. if (rectangle->bottom ^ (pd.pptfx[2].y - 1 & FIX_MASK))
  234. goto not_rectangle;
  235. rectangle->right = pd.pptfx[2].x - 1 & FIX_MASK;
  236. if (rectangle->right ^ (pd.pptfx[3].x - 1 & FIX_MASK))
  237. goto not_rectangle;
  238. }
  239. /* if the left is greater than the right then
  240. swap them so the blt code doesn't wig out */
  241. if (rectangle->left > rectangle->right) {
  242. FIX temp;
  243. temp = rectangle->left;
  244. rectangle->left = rectangle->right;
  245. rectangle->right = temp;
  246. }
  247. else {
  248. /* if left == right there's nothing to draw */
  249. if (rectangle->left == rectangle->right) {
  250. goto ReturnTrue;
  251. }
  252. }
  253. /* shift the values to get pixel coordinates */
  254. rectangle->left = (rectangle->left >> FIX_SHIFT) + 1;
  255. rectangle->right = (rectangle->right >> FIX_SHIFT) + 1;
  256. if (rectangle->top > rectangle->bottom) {
  257. FIX temp;
  258. temp = rectangle->top;
  259. rectangle->top = rectangle->bottom;
  260. rectangle->bottom = temp;
  261. }
  262. else {
  263. if (rectangle->top == rectangle->bottom) {
  264. goto ReturnTrue;
  265. }
  266. }
  267. /* shift the values to get pixel coordinates */
  268. rectangle->top = (rectangle->top >> FIX_SHIFT) + 1;
  269. rectangle->bottom = (rectangle->bottom >> FIX_SHIFT) + 1;
  270. // Finally, check for clipping
  271. if (jClipping == DC_RECT) {
  272. // Clip to the clip rectangle
  273. if (!bIntersectRect(rectangle, &ClipRect, rectangle)) {
  274. // Totally clipped, nothing to do
  275. goto ReturnTrue;
  276. }
  277. }
  278. /* if we get here then the polygon is a rectangle,
  279. set count to 1 and goto bottom to draw it */
  280. ulNumRects = 1;
  281. goto draw_remaining_rectangles;
  282. }
  283. not_rectangle:
  284. ;
  285. }
  286. // Do we have enough memory for all the edges?
  287. // LATER does cCurves include closure?
  288. if (ppo->cCurves > MAX_EDGES) {
  289. #if TAKING_ALLOC_STATS
  290. BufferMissInFillpath++;
  291. #endif
  292. //
  293. // try to allocate enough memory
  294. //
  295. pFreeEdges = (EDGE *) EngAllocMem(0, (ppo->cCurves * sizeof(EDGE)), ALLOC_TAG);
  296. if (pFreeEdges == NULL)
  297. {
  298. goto ReturnFalse; // too many edges; let GDI fill the path
  299. }
  300. else
  301. {
  302. bMemAlloced = TRUE;
  303. }
  304. }
  305. else {
  306. #if TAKING_ALLOC_STATS
  307. BufferHitInFillpath++;
  308. #endif
  309. pFreeEdges = pEdgeBuf; // use buffer on stack (it's big enough)
  310. }
  311. // Initialize an empty list of rectangles to fill
  312. ulNumRects = 0;
  313. // Enumerate the path edges and build a Global Edge Table (GET) from them
  314. // in YX-sorted order.
  315. pGETHead = &GETHead;
  316. if (!ConstructGET(pGETHead, pFreeEdges, ppo, &pd, bMore, &ClipRect)) {
  317. goto ReturnFalse; // outside GDI's 2**27 range
  318. }
  319. // Create an empty AET with the head node also a tail sentinel
  320. pAETHead = &AETHead;
  321. AETHead.pNext = pAETHead; // mark that the AET is empty
  322. AETHead.X = 0x7FFFFFFF; // this is greater than any valid X value, so
  323. // searches will always terminate
  324. // Top scan of polygon is the top of the first edge we come to
  325. iCurrentY = ((EDGE *)GETHead.pNext)->Y;
  326. // Loop through all the scans in the polygon, adding edges from the GET to
  327. // the Active Edge Table (AET) as we come to their starts, and scanning out
  328. // the AET at each scan into a rectangle list. Each time it fills up, the
  329. // rectangle list is passed to the filling routine, and then once again at
  330. // the end if any rectangles remain undrawn. We continue so long as there
  331. // are edges to be scanned out
  332. while (1) {
  333. // Advance the edges in the AET one scan, discarding any that have
  334. // reached the end (if there are any edges in the AET)
  335. if (AETHead.pNext != pAETHead) {
  336. AdvanceAETEdges(pAETHead);
  337. }
  338. // If the AET is empty, done if the GET is empty, else jump ahead to
  339. // the next edge in the GET; if the AET isn't empty, re-sort the AET
  340. if (AETHead.pNext == pAETHead) {
  341. if (GETHead.pNext == pGETHead) {
  342. // Done if there are no edges in either the AET or the GET
  343. break;
  344. }
  345. // There are no edges in the AET, so jump ahead to the next edge in
  346. // the GET
  347. iCurrentY = ((EDGE *)GETHead.pNext)->Y;
  348. } else {
  349. // Re-sort the edges in the AET by X coordinate, if there are at
  350. // least two edges in the AET (there could be one edge if the
  351. // balancing edge hasn't yet been added from the GET)
  352. if (((EDGE *)AETHead.pNext)->pNext != pAETHead) {
  353. XSortAETEdges(pAETHead);
  354. }
  355. }
  356. // Move any new edges that start on this scan from the GET to the AET;
  357. // bother calling only if there's at least one edge to add
  358. if (((EDGE *)GETHead.pNext)->Y == iCurrentY) {
  359. MoveNewEdges(pGETHead, pAETHead, iCurrentY);
  360. }
  361. // Scan the AET into rectangles to fill (there's always at least one
  362. // edge pair in the AET)
  363. pCurrentEdge = AETHead.pNext; // point to the first edge
  364. do {
  365. INT iLeftEdge;
  366. // The left edge of any given edge pair is easy to find; it's just
  367. // wherever we happen to be currently
  368. iLeftEdge = pCurrentEdge->X;
  369. // Find the matching right edge according to the current fill rule
  370. if ((flOptions & FP_WINDINGMODE) != 0) {
  371. INT iWindingCount;
  372. // Do winding fill; scan across until we've found equal numbers
  373. // of up and down edges
  374. iWindingCount = pCurrentEdge->iWindingDirection;
  375. do {
  376. pCurrentEdge = pCurrentEdge->pNext;
  377. iWindingCount += pCurrentEdge->iWindingDirection;
  378. } while (iWindingCount != 0);
  379. } else {
  380. // Odd-even fill; the next edge is the matching right edge
  381. pCurrentEdge = pCurrentEdge->pNext;
  382. }
  383. // See if the resulting span encompasses at least one pixel, and
  384. // add it to the list of rectangles to draw if so
  385. if (iLeftEdge < pCurrentEdge->X) {
  386. // We've got an edge pair to add to the list to be filled; see
  387. // if there's room for one more rectangle
  388. if (ulNumRects >= MAX_PATH_RECTS) {
  389. // No more room; draw the rectangles in the list and reset
  390. // it to empty
  391. (*pfnFill)(ppdev, ulNumRects, prclRects, mix, rbc,
  392. pptlBrush);
  393. // Reset the list to empty
  394. ulNumRects = 0;
  395. }
  396. // Add the rectangle representing the current edge pair
  397. if (jClipping == DC_RECT) {
  398. // Clipped
  399. // Clip to left
  400. prclRects[ulNumRects].left = max(iLeftEdge, ClipRect.left);
  401. // Clip to right
  402. prclRects[ulNumRects].right =
  403. min(pCurrentEdge->X, ClipRect.right);
  404. // Draw only if not fully clipped
  405. if (prclRects[ulNumRects].left <
  406. prclRects[ulNumRects].right) {
  407. prclRects[ulNumRects].top = iCurrentY;
  408. prclRects[ulNumRects].bottom = iCurrentY+1;
  409. ulNumRects++;
  410. }
  411. }
  412. else
  413. {
  414. // Unclipped
  415. prclRects[ulNumRects].top = iCurrentY;
  416. prclRects[ulNumRects].bottom = iCurrentY+1;
  417. prclRects[ulNumRects].left = iLeftEdge;
  418. prclRects[ulNumRects].right = pCurrentEdge->X;
  419. ulNumRects++;
  420. }
  421. }
  422. } while ((pCurrentEdge = pCurrentEdge->pNext) != pAETHead);
  423. iCurrentY++; // next scan
  424. }
  425. /* draw the remaining rectangles, if there are any */
  426. draw_remaining_rectangles:
  427. if (ulNumRects > 0) {
  428. (*pfnFill)(ppdev, ulNumRects, prclRects, mix, rbc, pptlBrush);
  429. }
  430. ReturnTrue:
  431. bRetVal = TRUE; // done successfully
  432. ReturnFalse:
  433. // bRetVal is originally false. If you jumped to ReturnFalse from somewhere,
  434. // then it will remain false, and be returned.
  435. if (bMemAlloced)
  436. {
  437. //
  438. // we did allocate memory, so release it
  439. //
  440. EngFreeMem (pFreeEdges);
  441. }
  442. return(bRetVal);
  443. }
  444. // Advance the edges in the AET to the next scan, dropping any for which we've
  445. // done all scans. Assumes there is at least one edge in the AET.
  446. VOID AdvanceAETEdges(EDGE *pAETHead)
  447. {
  448. EDGE *pLastEdge, *pCurrentEdge;
  449. pLastEdge = pAETHead;
  450. pCurrentEdge = pLastEdge->pNext;
  451. do {
  452. // Count down this edge's remaining scans
  453. if (--pCurrentEdge->iScansLeft == 0) {
  454. // We've done all scans for this edge; drop this edge from the AET
  455. pLastEdge->pNext = pCurrentEdge->pNext;
  456. } else {
  457. // Advance the edge's X coordinate for a 1-scan Y advance
  458. // Advance by the minimum amount
  459. pCurrentEdge->X += pCurrentEdge->iXWhole;
  460. // Advance the error term and see if we got one extra pixel this
  461. // time
  462. pCurrentEdge->iErrorTerm += pCurrentEdge->iErrorAdjustUp;
  463. if (pCurrentEdge->iErrorTerm >= 0) {
  464. // The error term turned over, so adjust the error term and
  465. // advance the extra pixel
  466. pCurrentEdge->iErrorTerm -= pCurrentEdge->iErrorAdjustDown;
  467. pCurrentEdge->X += pCurrentEdge->iXDirection;
  468. }
  469. pLastEdge = pCurrentEdge;
  470. }
  471. } while ((pCurrentEdge = pLastEdge->pNext) != pAETHead);
  472. }
  473. // X-sort the AET, because the edges may have moved around relative to
  474. // one another when we advanced them. We'll use a multipass bubble
  475. // sort, which is actually okay for this application because edges
  476. // rarely move relative to one another, so we usually do just one pass.
  477. // Also, this makes it easy to keep just a singly-linked list. Assumes there
  478. // are at least two edges in the AET.
  479. VOID XSortAETEdges(EDGE *pAETHead)
  480. {
  481. BOOL bEdgesSwapped;
  482. EDGE *pLastEdge, *pCurrentEdge, *pNextEdge;
  483. do {
  484. bEdgesSwapped = FALSE;
  485. pLastEdge = pAETHead;
  486. pCurrentEdge = pLastEdge->pNext;
  487. pNextEdge = pCurrentEdge->pNext;
  488. do {
  489. if (pNextEdge->X < pCurrentEdge->X) {
  490. // Next edge is to the left of the current edge; swap them
  491. pLastEdge->pNext = pNextEdge;
  492. pCurrentEdge->pNext = pNextEdge->pNext;
  493. pNextEdge->pNext = pCurrentEdge;
  494. bEdgesSwapped = TRUE;
  495. pCurrentEdge = pNextEdge; // continue sorting before the edge
  496. // we just swapped; it might move
  497. // farther yet
  498. }
  499. pLastEdge = pCurrentEdge;
  500. pCurrentEdge = pLastEdge->pNext;
  501. } while ((pNextEdge = pCurrentEdge->pNext) != pAETHead);
  502. } while (bEdgesSwapped);
  503. }
  504. // Moves all edges that start on the current scan from the GET to the AET in
  505. // X-sorted order. Parameters are pointer to head of GET and pointer to dummy
  506. // edge at head of AET, plus current scan line. Assumes there's at least one
  507. // edge to be moved.
  508. VOID MoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, INT iCurrentY)
  509. {
  510. EDGE *pCurrentEdge = pAETHead;
  511. EDGE *pGETNext = pGETHead->pNext;
  512. do {
  513. // Scan through the AET until the X-sorted insertion point for this
  514. // edge is found. We can continue from where the last search left
  515. // off because the edges in the GET are in X sorted order, as is
  516. // the AET. The search always terminates because the AET sentinel
  517. // is greater than any valid X
  518. while (pGETNext->X > ((EDGE *)pCurrentEdge->pNext)->X) {
  519. pCurrentEdge = pCurrentEdge->pNext;
  520. }
  521. // We've found the insertion point; add the GET edge to the AET, and
  522. // remove it from the GET
  523. pGETHead->pNext = pGETNext->pNext;
  524. pGETNext->pNext = pCurrentEdge->pNext;
  525. pCurrentEdge->pNext = pGETNext;
  526. pCurrentEdge = pGETNext; // continue insertion search for the next
  527. // GET edge after the edge we just added
  528. pGETNext = pGETHead->pNext;
  529. } while (pGETNext->Y == iCurrentY);
  530. }
  531. // Build the Global Edge Table from the path. There must be enough memory in
  532. // the free edge area to hold all edges. The GET is constructed in Y-X order,
  533. // and has a head/tail/sentinel node at pGETHead.
  534. BOOL ConstructGET(
  535. EDGE *pGETHead,
  536. EDGE *pFreeEdges,
  537. PATHOBJ *ppo,
  538. PATHDATA *pd,
  539. BOOL bMore,
  540. RECTL *pClipRect)
  541. {
  542. POINTFIX pfxPathStart; // point that started the current subpath
  543. POINTFIX pfxPathPrevious; // point before the current point in a subpath;
  544. // starts the current edge
  545. /* Create an empty GET with the head node also a tail sentinel */
  546. pGETHead->pNext = pGETHead; // mark that the GET is empty
  547. pGETHead->Y = 0x7FFFFFFF; // this is greater than any valid Y value, so
  548. // searches will always terminate
  549. /* PATHOBJ_vEnumStart is implicitly performed by engine
  550. already and first path is enumerated by the caller */
  551. next_subpath:
  552. /* Make sure the PATHDATA is not empty (is this necessary) */
  553. if (pd->count != 0) {
  554. /* If first point starts a subpath, remember it as such
  555. and go on to the next point, so we can get an edge */
  556. if (pd->flags & PD_BEGINSUBPATH) {
  557. /* the first point starts the subpath; remember it */
  558. pfxPathStart = *pd->pptfx; /* the subpath starts here */
  559. pfxPathPrevious = *pd->pptfx; /* this points starts the next edge */
  560. pd->pptfx++; /* advance to the next point */
  561. pd->count--; /* count off this point */
  562. }
  563. /* add edges in PATHDATA to GET, in Y-X sorted order */
  564. while (pd->count--) {
  565. if ((pFreeEdges =
  566. AddEdgeToGET(pGETHead, pFreeEdges, &pfxPathPrevious, pd->pptfx,
  567. pClipRect)) == NULL) {
  568. goto ReturnFalse;
  569. }
  570. pfxPathPrevious = *pd->pptfx; /* current point becomes previous */
  571. pd->pptfx++; /* advance to the next point */
  572. }
  573. /* If last point ends the subpath, insert the edge that
  574. connects to first point (is this built in already?) */
  575. if (pd->flags & PD_ENDSUBPATH) {
  576. if ((pFreeEdges = AddEdgeToGET(pGETHead, pFreeEdges, &pfxPathPrevious,
  577. &pfxPathStart, pClipRect)) == NULL) {
  578. goto ReturnFalse;
  579. }
  580. }
  581. }
  582. /* the initial loop conditions preclude a do, while or for */
  583. if (bMore) {
  584. bMore = PATHOBJ_bEnum(ppo, pd);
  585. goto next_subpath;
  586. }
  587. return(TRUE); // done successfully
  588. ReturnFalse:
  589. return(FALSE); // failed
  590. }
  591. // Adds the edge described by the two passed-in points to the Global Edge
  592. // Table, if the edge spans at least one pixel vertically.
  593. EDGE * AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge,
  594. POINTFIX *ppfxEdgeStart, POINTFIX *ppfxEdgeEnd, RECTL *pClipRect)
  595. {
  596. INT iYStart, iYEnd, iXStart, iXEnd, iYHeight, iXWidth;
  597. INT yJump, yTop;
  598. // Set the winding-rule direction of the edge, and put the endpoints in
  599. // top-to-bottom order
  600. iYHeight = ppfxEdgeEnd->y - ppfxEdgeStart->y;
  601. if (iYHeight == 0) {
  602. return(pFreeEdge); // zero height; ignore this edge
  603. } else if (iYHeight >= 0) {
  604. iXStart = ppfxEdgeStart->x;
  605. iYStart = ppfxEdgeStart->y;
  606. iXEnd = ppfxEdgeEnd->x;
  607. iYEnd = ppfxEdgeEnd->y;
  608. pFreeEdge->iWindingDirection = 1;
  609. } else {
  610. iYHeight = -iYHeight;
  611. iXEnd = ppfxEdgeStart->x;
  612. iYEnd = ppfxEdgeStart->y;
  613. iXStart = ppfxEdgeEnd->x;
  614. iYStart = ppfxEdgeEnd->y;
  615. pFreeEdge->iWindingDirection = -1;
  616. }
  617. if (iYHeight & 0x80000000) {
  618. return(NULL); // too large; outside 2**27 GDI range
  619. }
  620. // Set the error term and adjustment factors, all in GIQ coordinates for
  621. // now
  622. iXWidth = iXEnd - iXStart;
  623. if (iXWidth >= 0) {
  624. // Left to right, so we change X as soon as we move at all
  625. pFreeEdge->iXDirection = 1;
  626. pFreeEdge->iErrorTerm = -1;
  627. } else {
  628. // Right to left, so we don't change X until we've moved a full GIQ
  629. // coordinate
  630. iXWidth = -iXWidth;
  631. pFreeEdge->iXDirection = -1;
  632. pFreeEdge->iErrorTerm = -iYHeight;
  633. }
  634. if (iXWidth & 0x80000000) {
  635. return(NULL); // too large; outside 2**27 GDI range
  636. }
  637. if (iXWidth >= iYHeight) {
  638. // Calculate base run length (minimum distance advanced in X for a 1-
  639. // scan advance in Y)
  640. pFreeEdge->iXWhole = iXWidth / iYHeight;
  641. // Add sign back into base run length if going right to left
  642. if (pFreeEdge->iXDirection == -1) {
  643. pFreeEdge->iXWhole = -pFreeEdge->iXWhole;
  644. }
  645. pFreeEdge->iErrorAdjustUp = iXWidth % iYHeight;
  646. } else {
  647. // Base run length is 0, because line is closer to vertical than
  648. // horizontal
  649. pFreeEdge->iXWhole = 0;
  650. pFreeEdge->iErrorAdjustUp = iXWidth;
  651. }
  652. pFreeEdge->iErrorAdjustDown = iYHeight;
  653. // Calculate the number of pixels spanned by this edge, accounting for
  654. // clipping
  655. // Top true pixel scan in GIQ coordinates
  656. // Shifting to divide and multiply by 16 is okay because the clip rect
  657. // always contains positive numbers
  658. yTop = max(pClipRect->top << 4, (iYStart + 15) & ~0x0F);
  659. pFreeEdge->Y = yTop >> 4; // initial scan line on which to fill edge
  660. // Calculate # of scans to actually fill, accounting for clipping
  661. if ((pFreeEdge->iScansLeft = min(pClipRect->bottom, ((iYEnd + 15) >> 4))
  662. - pFreeEdge->Y) <= 0) {
  663. return(pFreeEdge); // no pixels at all are spanned, so we can
  664. // ignore this edge
  665. }
  666. // If the edge doesn't start on a pixel scan (that is, it starts at a
  667. // fractional GIQ coordinate), advance it to the first pixel scan it
  668. // intersects. Ditto if there's top clipping. Also clip to the bottom if
  669. // needed
  670. if (iYStart != yTop) {
  671. // Jump ahead by the Y distance in GIQ coordinates to the first pixel
  672. // to draw
  673. yJump = yTop - iYStart;
  674. // Advance x the minimum amount for the number of scans traversed
  675. iXStart += pFreeEdge->iXWhole * yJump;
  676. AdjustErrorTerm(&pFreeEdge->iErrorTerm, pFreeEdge->iErrorAdjustUp,
  677. pFreeEdge->iErrorAdjustDown, yJump, &iXStart,
  678. pFreeEdge->iXDirection);
  679. }
  680. // Turn the calculations into pixel rather than GIQ calculations
  681. // Move the X coordinate to the nearest pixel, and adjust the error term
  682. // accordingly
  683. // Dividing by 16 with a shift is okay because X is always positive
  684. pFreeEdge->X = (iXStart + 15) >> 4; // convert from GIQ to pixel coordinates
  685. // LATER adjust only if needed (if prestepped above)?
  686. if (pFreeEdge->iXDirection == 1) {
  687. // Left to right
  688. pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
  689. (((iXStart + 15) & ~0x0F) - iXStart);
  690. } else {
  691. // Right to left
  692. pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
  693. ((iXStart - 1) & 0x0F);
  694. }
  695. // Scale the error term down 16 times to switch from GIQ to pixels.
  696. // Shifts work to do the multiplying because these values are always
  697. // non-negative
  698. pFreeEdge->iErrorTerm >>= 4;
  699. // Insert the edge into the GET in YX-sorted order. The search always ends
  700. // because the GET has a sentinel with a greater-than-possible Y value
  701. while ((pFreeEdge->Y > ((EDGE *)pGETHead->pNext)->Y) ||
  702. ((pFreeEdge->Y == ((EDGE *)pGETHead->pNext)->Y) &&
  703. (pFreeEdge->X > ((EDGE *)pGETHead->pNext)->X))) {
  704. pGETHead = pGETHead->pNext;
  705. }
  706. pFreeEdge->pNext = pGETHead->pNext; // link the edge into the GET
  707. pGETHead->pNext = pFreeEdge;
  708. return(++pFreeEdge); // point to the next edge storage location for next
  709. // time
  710. }
  711. // Adjust the error term for a skip ahead in y. This is in ASM because there's
  712. // a multiply/divide that may involve a larger than 32-bit value.
  713. void AdjustErrorTerm(INT *pErrorTerm, INT iErrorAdjustUp, INT iErrorAdjustDown,
  714. INT yJump, INT *pXStart, INT iXDirection)
  715. {
  716. #if defined(_X86_) || defined(i386)
  717. // Adjust the error term up by the number of y coordinates we'll skip
  718. //*pErrorTerm += iErrorAdjustUp * yJump;
  719. _asm mov ebx,pErrorTerm
  720. _asm mov eax,iErrorAdjustUp
  721. _asm mul yJump
  722. _asm add eax,[ebx]
  723. _asm adc edx,-1 // the error term starts out negative
  724. // See if the error term turned over even once while skipping
  725. //if (*pErrorTerm >= 0) {
  726. _asm js short NoErrorTurnover
  727. // # of times we'll turn over the error term and step an extra x
  728. // coordinate while skipping
  729. // NumAdjustDowns = (*pErrorTerm / iErrorAdjustDown) + 1;
  730. _asm div iErrorAdjustDown
  731. _asm inc eax
  732. // Note that EDX is the remainder; (EDX - iErrorAdjustDown) is where
  733. // the error term ends up ultimately
  734. // Advance x appropriately for the # of times the error term
  735. // turned over
  736. // if (iXDirection == 1) {
  737. // *pXStart += NumAdjustDowns;
  738. // } else {
  739. // *pXStart -= NumAdjustDowns;
  740. // }
  741. _asm mov ecx,pXStart
  742. _asm cmp iXDirection,1
  743. _asm jz short GoingRight
  744. _asm neg eax
  745. GoingRight:
  746. _asm add [ecx],eax
  747. // Adjust the error term down to its proper post-skip value
  748. // *pErrorTerm -= iErrorAdjustDown * NumAdjustDowns;
  749. _asm sub edx,iErrorAdjustDown
  750. _asm mov eax,edx // put into EAX for storing to pErrorTerm next
  751. // }
  752. NoErrorTurnover:
  753. _asm mov [ebx],eax
  754. #else
  755. INT NumAdjustDowns;
  756. // Adjust the error term up by the number of y coordinates we'll skip
  757. *pErrorTerm += iErrorAdjustUp * yJump;
  758. // See if the error term turned over even once while skipping
  759. if (*pErrorTerm >= 0) {
  760. // # of times we'll turn over the error term and step an extra x
  761. // coordinate while skipping
  762. NumAdjustDowns = (*pErrorTerm / iErrorAdjustDown) + 1;
  763. // Advance x appropriately for the # of times the error term
  764. // turned over
  765. if (iXDirection == 1) {
  766. *pXStart += NumAdjustDowns;
  767. } else {
  768. *pXStart -= NumAdjustDowns;
  769. }
  770. // Adjust the error term down to its proper post-skip value
  771. *pErrorTerm -= iErrorAdjustDown * NumAdjustDowns;
  772. }
  773. #endif
  774. }