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.

2114 lines
61 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: draweng.cxx
  3. *
  4. * Internal helper functions for GDI draw calls.
  5. *
  6. * Created: 19-Nov-1990
  7. * Author: J. Andrew Goossen [andrewgo]
  8. *
  9. * Copyright (c) 1990-1999 Microsoft Corporation
  10. *
  11. \**************************************************************************/
  12. #include "precomp.hxx"
  13. #include "flhack.hxx"
  14. #if DBG
  15. LONG lConv(EFLOAT ef)
  16. {
  17. LONG l;
  18. ef *= FP_1000_0;
  19. ef.bEfToL(l);
  20. return(l);
  21. }
  22. #endif
  23. /******************************Public*Routine******************************\
  24. * EFLOAT efHalfDiff(a, b)
  25. *
  26. * Computes (a - b) / 2 without overflow or loss of precision.
  27. *
  28. * History:
  29. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  30. * Wrote it.
  31. \**************************************************************************/
  32. inline EFLOAT efHalfDiff(LONG a, LONG b)
  33. {
  34. EFLOATEXT efResult((a >> 1) - (b >> 1));
  35. if ((a ^ b) & 1)
  36. {
  37. if (a & 1)
  38. efResult += FP_0_5;
  39. else
  40. efResult -= FP_0_5;
  41. }
  42. return(efResult);
  43. }
  44. /******************************Public*Routine******************************\
  45. * EFLOAT efMid(a, b)
  46. *
  47. * Computes (a + b) / 2 without overflow or loss of precision. Note that
  48. * we can't convert 'a' and 'b' to floats and then add them because we're
  49. * not guaranteed that an 'EFLOAT' will have a mantissa with more than
  50. * the 32 bit precision of a LONG.
  51. *
  52. * History:
  53. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  54. * Wrote it.
  55. \**************************************************************************/
  56. inline EFLOAT efMid(LONG a, LONG b)
  57. {
  58. return(efHalfDiff(a, -b));
  59. }
  60. /******************************Public*Routine******************************\
  61. * EFLOAT efHalf(ul)
  62. *
  63. * Compute half of 'ul' without overflow or loss of precision.
  64. *
  65. * History:
  66. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  67. * Wrote it.
  68. \**************************************************************************/
  69. inline EFLOAT efHalf(ULONG ul)
  70. {
  71. EFLOATEXT efResult((LONG) (ul >> 1));
  72. if (ul & 1)
  73. efResult += FP_0_5;
  74. return(efResult);
  75. }
  76. /******************************Public*Routine******************************\
  77. * VOID vHalf(ptl)
  78. *
  79. * Halves the given vector. Rounds .5 fractions up. Assumes it's in FIX
  80. * format so that we don't have to worry about overflow.
  81. *
  82. * History:
  83. * 19-Dec-1990 -by- J. Andrew Goossen [andrewgo]
  84. * Wrote it.
  85. \**************************************************************************/
  86. inline VOID vHalf(POINTL& ptl)
  87. {
  88. ptl.x = (ptl.x + 1) >> 1;
  89. ptl.y = (ptl.y + 1) >> 1;
  90. }
  91. /******************************Public*Routine******************************\
  92. * EBOX::EBOX(ercl)
  93. *
  94. * EBOX Constructor for figures created by Create-region APIs.
  95. *
  96. * History:
  97. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  98. * Wrote it.
  99. \**************************************************************************/
  100. EBOX::EBOX(ERECTL& ercl, BOOL bFillEllipse)
  101. {
  102. ercl.vOrder();
  103. rclWorld = ercl;
  104. bIsEmpty = FALSE;
  105. bIsFillInsideFrame = FALSE;
  106. // Do the Win3 silliness and make the box lower-right exclusive
  107. // (remember that regions are already lower-right exclusive, so this
  108. // will double the exclusiveness...)
  109. aeptl[0].x = LTOFX(ercl.right - 1);
  110. aeptl[0].y = LTOFX(ercl.top);
  111. aeptl[2].x = LTOFX(ercl.left);
  112. aeptl[2].y = LTOFX(ercl.bottom - 1);
  113. // If this will be a filled ellipse, we bump up the size in all
  114. // dimensions to get a nicer looking fill:
  115. if (bFillEllipse)
  116. {
  117. aeptl[0].x += GROW_ELLIPSE_SIZE - LTOFX(1);
  118. aeptl[0].y -= GROW_ELLIPSE_SIZE;
  119. aeptl[2].x -= GROW_ELLIPSE_SIZE;
  120. aeptl[2].y += GROW_ELLIPSE_SIZE - LTOFX(1);
  121. }
  122. aeptl[1].y = aeptl[0].y;
  123. aeptl[1].x = aeptl[2].x;
  124. aeptl[3].x = aeptl[0].x;
  125. aeptl[3].y = aeptl[2].y;
  126. eptlA.x = ((aeptl[0].x - aeptl[1].x) + 1) >> 1;
  127. eptlA.y = 0;
  128. eptlB.x = 0;
  129. eptlB.y = ((aeptl[1].y - aeptl[2].y) + 1) >> 1;
  130. eptlOrigin = aeptl[2];
  131. eptlOrigin += eptlA;
  132. eptlOrigin += eptlB;
  133. }
  134. /******************************Public*Routine******************************\
  135. * EBOX::EBOX(exo, rcl)
  136. *
  137. * EBOX Constructor for figures that don't need lower-right exclusion and
  138. * PS_INSIDEFRAME functionality. Rectangle must already be well-ordered
  139. * so that (top, left) is the upper-left corner of the box when
  140. * the World-to-Page transform is identity and the rectangle is transformed
  141. * to device coordinates.
  142. *
  143. * History:
  144. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  145. * Wrote it.
  146. \**************************************************************************/
  147. EBOX::EBOX(
  148. EXFORMOBJ& exo,
  149. RECTL& rcl)
  150. {
  151. rclWorld = rcl;
  152. bIsEmpty = FALSE;
  153. bIsFillInsideFrame = FALSE;
  154. aeptl[0].x = rcl.right;
  155. aeptl[0].y = rcl.top;
  156. aeptl[1].x = rcl.left;
  157. aeptl[1].y = rcl.top;
  158. aeptl[2].x = rcl.left;
  159. aeptl[2].y = rcl.bottom;
  160. exo.bXformRound(aeptl, (PPOINTFIX) aeptl, 3);
  161. eptlA = aeptl[0];
  162. eptlA -= aeptl[1];
  163. eptlB = aeptl[1];
  164. eptlB -= aeptl[2];
  165. aeptl[3] = aeptl[2];
  166. aeptl[3] += eptlA;
  167. vHalf(eptlA);
  168. vHalf(eptlB);
  169. eptlOrigin = aeptl[2];
  170. eptlOrigin += eptlA;
  171. eptlOrigin += eptlB;
  172. }
  173. /******************************Public*Routine******************************\
  174. * EBOX::EBOX(dco, rclBox, pla, bFillEllipse)
  175. *
  176. * Constructor for figures that need lower-right exclusion and
  177. * PS_INSIDEFRAME functionality.
  178. *
  179. * History:
  180. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  181. * Wrote it.
  182. \**************************************************************************/
  183. EBOX::EBOX(DCOBJ& dco, RECTL& rclBox, LINEATTRS *pla, BOOL bFillEllipse)
  184. {
  185. rclWorld = rclBox;
  186. bIsEmpty = FALSE;
  187. bIsFillInsideFrame = FALSE;
  188. //Shift the rect one pixcel if the dc is mirrored
  189. if (MIRRORED_DC(dco.pdc)) {
  190. --rclWorld.left;
  191. --rclWorld.right;
  192. }
  193. if (dco.pdc->iGraphicsMode() == GM_ADVANCED)
  194. {
  195. // If we're in advanced mode, we always draw counterclockwise in
  196. // logical space:
  197. ((ERECTL*)&rclWorld)->vOrder();
  198. }
  199. else
  200. {
  201. register LONG lTmp;
  202. // Win3 always draws counterclockwise in device space; this means
  203. // we might be drawing clockwise in logical space. We have to be
  204. // compatible.
  205. //
  206. // There is the additional problem that metafiles may apply a
  207. // rotating transform on top; we have to correctly rotate the Win3
  208. // result
  209. //
  210. // Order the points so that with an identity world-to-page transform,
  211. // the drawing direction will always be counterclockwise (or clock-
  212. // wise, if the DC bit is set) in device space, regardless of the
  213. // page-to-device transform (Win3 always draws counterclockwise):
  214. if ((dco.pdc->befM11IsNegative() && (rclWorld.left < rclWorld.right)) ||
  215. (!dco.pdc->befM11IsNegative() && (rclWorld.left > rclWorld.right)))
  216. {
  217. SWAPL(rclWorld.left, rclWorld.right, lTmp);
  218. }
  219. if ((dco.pdc->befM22IsNegative() && (rclWorld.top < rclWorld.bottom)) ||
  220. (!dco.pdc->befM22IsNegative() && (rclWorld.top > rclWorld.bottom)))
  221. {
  222. SWAPL(rclWorld.bottom, rclWorld.top, lTmp);
  223. }
  224. }
  225. // To simplify things, we've assumed we'll be drawing counter-clockwise
  226. // (in logical space if in Advanced mode, in device space if in Compatibility
  227. // mode). We now check the SetArcDirection setting; if it says to draw
  228. // clockwise we merely have to flip our bound box upside down:
  229. if (dco.pdc->bClockwise())
  230. {
  231. register LONG lTmp;
  232. SWAPL(rclWorld.top, rclWorld.bottom, lTmp);
  233. }
  234. ERECTL ercl(rclWorld);
  235. // It was decided that the PS_INSIDEFRAME attribute of the
  236. // pen current when the call is done is to be used. That is,
  237. // when accumulating a path, the pen that is current when the
  238. // figure call is done is used for PS_INSIDEFRAME; all other
  239. // pen attributes of the path come from the pen that is active
  240. // when the path is stroked or filled.
  241. PPEN ppen = (PPEN) dco.pdc->pbrushLine();
  242. EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
  243. BOOL bInsideFrame = (ppen->bIsInsideFrame() && (pla->fl & LA_GEOMETRIC));
  244. if (bInsideFrame)
  245. {
  246. // We have to be careful of overflow because we're dealing with
  247. // world space coordinates, which may use all 32 bits:
  248. EFLOAT efHalfPen = efHalf(ppen->lWidthPen());
  249. EFLOAT efdx = efHalfDiff(ercl.left, ercl.right);
  250. EFLOAT efdy = efHalfDiff(ercl.top, ercl.bottom);
  251. efdx.vAbs();
  252. efdy.vAbs();
  253. // PS_INSIDEFRAME is plain dumb. What happens when the bounding
  254. // box is smaller in dimension than the width of the pen? For
  255. // Ellipses, Rectangles and RoundRects, we'll Fill instead of
  256. // StrokeAndFill'ing. (Do nothing if this occurs for Arcs, Chords
  257. // or Pies, just like Win3 does.)
  258. if (efHalfPen > efdx || efHalfPen > efdy)
  259. {
  260. bIsFillInsideFrame = TRUE;
  261. bInsideFrame = FALSE;
  262. }
  263. }
  264. // In Win3, figures are lower-right exclusive in device space. This
  265. // convention is hard to maintain with the introduction of arbitrary
  266. // affine transforms (like rotations). If the GraphicsMode has been
  267. // set to advanced, or we're doing an PS_INSIDEFRAME pen, we're
  268. // lower-right inclusive; otherwise we're lower-right exclusive.
  269. //
  270. // The metafile code is able to set a world transform without going
  271. // to advanced mode, so we check for that too:
  272. if (dco.pdc->iGraphicsMode() == GM_ADVANCED ||
  273. bInsideFrame ||
  274. bIsFillInsideFrame ||
  275. dco.pdc->flXform() & WORLD_TRANSFORM_SET)
  276. {
  277. // We're lower-right inclusive:
  278. aeptl[0].x = ercl.right;
  279. aeptl[0].y = ercl.top;
  280. aeptl[1].x = ercl.left;
  281. aeptl[1].y = ercl.top;
  282. aeptl[2].x = ercl.left;
  283. aeptl[2].y = ercl.bottom;
  284. exo.bXformRound(aeptl, (PPOINTFIX) aeptl, 3);
  285. // HEURISTIC: For a filled ellipse, if the corners of the bound
  286. // box are on the integer grid, we expand it a bit so that we get
  287. // a nicer, more symmetric fill according to our filling conventions:
  288. if (bFillEllipse && ppen->flStylePen() == PS_NULL &&
  289. ((aeptl[0].x | aeptl[0].y | aeptl[2].x | aeptl[2].y) &
  290. (LTOFX(1) - 1)) == 0)
  291. {
  292. register LONG lDelta;
  293. lDelta = (aeptl[0].x > aeptl[2].x) ?
  294. GROW_ELLIPSE_SIZE :
  295. -GROW_ELLIPSE_SIZE;
  296. aeptl[0].x += lDelta;
  297. aeptl[1].x -= lDelta;
  298. aeptl[2].x -= lDelta;
  299. lDelta = (aeptl[2].y > aeptl[0].y) ?
  300. GROW_ELLIPSE_SIZE :
  301. -GROW_ELLIPSE_SIZE;
  302. aeptl[0].y -= lDelta;
  303. aeptl[1].y -= lDelta;
  304. aeptl[2].y += lDelta;
  305. }
  306. }
  307. else
  308. {
  309. // Since the DC is not lower right inclusive, it means that we
  310. // have a simple transform: scaling and translation only.
  311. exo.bXformRound((PPOINTL) &ercl, (PPOINTFIX) &ercl, 2);
  312. LONG cShrink = LTOFX(1);
  313. // HEURISTIC: For a filled ellipse, if the corners of the bound
  314. // box are on the integer grid, we expand it a bit so that we get
  315. // a nicer, more symmetric fill according to our filling conventions:
  316. if (bFillEllipse && ppen->flStylePen() == PS_NULL &&
  317. (((ercl.right | ercl.bottom | ercl.left | ercl.top) &
  318. (LTOFX(1) - 1)) == 0))
  319. {
  320. register LONG lDelta;
  321. lDelta = (ercl.right > ercl.left) ?
  322. GROW_ELLIPSE_SIZE :
  323. -GROW_ELLIPSE_SIZE;
  324. ercl.right += lDelta;
  325. ercl.left -= lDelta;
  326. lDelta = (ercl.bottom > ercl.top) ?
  327. GROW_ELLIPSE_SIZE :
  328. -GROW_ELLIPSE_SIZE;
  329. ercl.top -= lDelta;
  330. ercl.bottom += lDelta;
  331. // We have to shrink by two pixels to be more compatible with Win3
  332. // in this case:
  333. cShrink = LTOFX(2);
  334. }
  335. LONG dx = ercl.right - ercl.left;
  336. LONG dy = ercl.bottom - ercl.top;
  337. if (ABS(dx) < cShrink || ABS(dy) < cShrink)
  338. {
  339. bIsEmpty = TRUE;
  340. return;
  341. }
  342. // Shrink the bounding box for the lower-right exclusivity.
  343. if (dx > 0)
  344. ercl.right -= cShrink;
  345. else
  346. ercl.left -= cShrink;
  347. if (dy > 0)
  348. ercl.bottom -= cShrink;
  349. else
  350. ercl.top -= cShrink;
  351. // It makes no sense to do this when accumulating a path:
  352. // when the path is to be converted to a region, we have
  353. // no idea what orientation of the transform is. That is,
  354. // we can't be sure what sides to push in (we have no idea
  355. // what the transform will be when the region is painted or
  356. // whatever). Oh well: win3.x compatibility rules!
  357. aeptl[0].x = ercl.right;
  358. aeptl[0].y = ercl.top;
  359. aeptl[1].x = ercl.left;
  360. aeptl[1].y = ercl.top;
  361. aeptl[2].x = ercl.left;
  362. aeptl[2].y = ercl.bottom;
  363. }
  364. // Widelines in Win3 are so broken that it's unclear if the
  365. // PS_INSIDEFRAME adjustment takes lower-right exclusion into
  366. // account. We let the region lower-right exclusion take care
  367. // of it.
  368. if (bInsideFrame)
  369. {
  370. // We handle the PS_INSIDEFRAME attribute by shrinking the
  371. // bound box by half the pen width on all sides. This must
  372. // be done in device space, otherwise we would lose accuracy.
  373. //
  374. // As such, the box is now only a parallelogram as it may
  375. // have been sheered, etc., and so we must push in the corners
  376. // using vectors.
  377. EAPOINTL avecCorner[2];
  378. avecCorner[1].x = avecCorner[1].y = ppen->lWidthPen();
  379. // Orient the world space vector avecCorner[1] so that in world space
  380. // it points from the top, left corner of the bound box towards
  381. // the center of the box:
  382. if (rclWorld.right < rclWorld.left)
  383. avecCorner[1].x = -avecCorner[1].x;
  384. if (rclWorld.bottom < rclWorld.top)
  385. avecCorner[1].y = -avecCorner[1].y;
  386. // Now set avecCorner[0] so that in world space it points from the
  387. // top, right corner of the bound box towards the center:
  388. avecCorner[0].x = -avecCorner[1].x;
  389. avecCorner[0].y = avecCorner[1].y;
  390. // This transform shouldn't fail because we've already stripped
  391. // the MSBs of ptlPen:
  392. exo.bXform((PVECTORL) avecCorner, (PVECTORFX) avecCorner, 2);
  393. // We push in the box by only half the pen width, so halve the
  394. // corner vectors:
  395. vHalf(avecCorner[0]);
  396. vHalf(avecCorner[1]);
  397. // We push in all the corners of the bound box by the corner vectors:
  398. aeptl[0] += avecCorner[0];
  399. aeptl[1] += avecCorner[1];
  400. aeptl[2] -= avecCorner[0];
  401. }
  402. eptlA = aeptl[0];
  403. eptlA -= aeptl[1];
  404. eptlB = aeptl[1];
  405. eptlB -= aeptl[2];
  406. aeptl[3] = aeptl[2];
  407. aeptl[3] += eptlA;
  408. vHalf(eptlA);
  409. vHalf(eptlB);
  410. eptlOrigin = aeptl[2];
  411. eptlOrigin += eptlA;
  412. eptlOrigin += eptlB;
  413. }
  414. /******************************Public*Routine******************************\
  415. * EBOX::ptlXform(ptef)
  416. *
  417. * Transforms a point constructed on the unit circle centered at the
  418. * origin to the ellipse described by the bounding box.
  419. *
  420. * (A.x A.y )
  421. * (x' y') = (x y 1) (B.x B.y )
  422. * (Origin.x Origin.y)
  423. *
  424. * History:
  425. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  426. * Wrote it.
  427. \**************************************************************************/
  428. POINTL EBOX::ptlXform(EPOINTFL& ptef)
  429. {
  430. EPOINTL eptl;
  431. EFLOATEXT efTerm1(eptlA.x);
  432. EFLOATEXT efTerm2(eptlB.x);
  433. efTerm1 *= ptef.x;
  434. efTerm2 *= ptef.y;
  435. efTerm1 += efTerm2;
  436. efTerm1.bEfToL(eptl.x);
  437. efTerm1 = eptlA.y;
  438. efTerm2 = eptlB.y;
  439. efTerm1 *= ptef.x;
  440. efTerm2 *= ptef.y;
  441. efTerm1 += efTerm2;
  442. efTerm1.bEfToL(eptl.y);
  443. eptl += eptlOrigin;
  444. return(eptl);
  445. }
  446. /******************************Public*Routine******************************\
  447. * VOID vArctan(x, y, efTheta, lQuadrant)
  448. *
  449. * Returns the Arctangent angle in degrees. Uses a look-up table with
  450. * linear interpolation. Accuracy is kinda good, I guess, with a table
  451. * size of 32. Returns the quadrant of the angle (0 through 3).
  452. *
  453. * History:
  454. * Wed 23-Oct-1991 09:39:21 by Kirk Olynyk [kirko]
  455. * This routine is now used in FONTMAP.CXX. Please be careful when
  456. * modifying.
  457. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  458. * Wrote it.
  459. \**************************************************************************/
  460. const BYTE gajArctanQuadrant[] = { 0, 1, 3, 2, 0, 1, 3, 2 };
  461. VOID vArctan
  462. (
  463. EFLOAT x,
  464. EFLOAT y,
  465. EFLOAT& efTheta,
  466. LONG& lQuadrant
  467. )
  468. {
  469. LONG lOctant = 0;
  470. if (x.bIsNegative())
  471. {
  472. x.vNegate();
  473. lOctant |= NEGATE_X;
  474. }
  475. if (y.bIsNegative())
  476. {
  477. y.vNegate();
  478. lOctant |= NEGATE_Y;
  479. }
  480. if (y > x)
  481. {
  482. EFLOAT ef = x;
  483. x = y;
  484. y = ef;
  485. lOctant |= SWITCH_X_AND_Y;
  486. }
  487. // If x == 0 and y == 0, Arctan is undefined. May as well return 0.
  488. if (x.bIsZero())
  489. {
  490. efTheta = FP_0_0;
  491. lQuadrant = 0;
  492. return;
  493. }
  494. // Calculate efIndex = (y / x) * ARCTAN_TABLE_SIZE:
  495. EFLOAT efIndex = y;
  496. efIndex *= FP_ARCTAN_TABLE_SIZE;
  497. efIndex /= x;
  498. // lIndex = floor(efIndex):
  499. LONG lIndex;
  500. efIndex.bEfToLTruncate(lIndex);
  501. // efDelta = fraction(efIndex):
  502. EFLOAT efDelta;
  503. efIndex.vFraction(efDelta);
  504. ASSERTGDI(lIndex >= 0 && lIndex <= ARCTAN_TABLE_SIZE + 1,
  505. "Arctan: Index out of bounds\n");
  506. ASSERTGDI(!efDelta.bIsNegative() && FP_1_0 > efDelta,
  507. "Arctan: Delta out of bounds\n");
  508. // gaefArctan has an extra 0 at the end of the table so that
  509. // calculations for slope == 1 don't require special case code.
  510. //
  511. // efTheta = gaefArctan[lIndex]
  512. // + efDelta * (gaefArctan[lIndex + 1] - gaefArctan[lIndex]):
  513. efTheta = gaefArctan[lIndex + 1];
  514. efTheta -= gaefArctan[lIndex];
  515. efTheta *= efDelta;
  516. efTheta += gaefArctan[lIndex];
  517. switch (lOctant)
  518. {
  519. case OCTANT_1:
  520. {
  521. efTheta.vNegate();
  522. efTheta += FP_90_0;
  523. break;
  524. }
  525. case OCTANT_2:
  526. {
  527. efTheta += FP_90_0;
  528. break;
  529. }
  530. case OCTANT_3:
  531. {
  532. efTheta.vNegate();
  533. efTheta += FP_180_0;
  534. break;
  535. }
  536. case OCTANT_4:
  537. {
  538. efTheta += FP_180_0;
  539. break;
  540. }
  541. case OCTANT_5:
  542. {
  543. efTheta.vNegate();
  544. efTheta += FP_270_0;
  545. break;
  546. }
  547. case OCTANT_6:
  548. {
  549. efTheta += FP_270_0;
  550. break;
  551. }
  552. case OCTANT_7:
  553. {
  554. efTheta.vNegate();
  555. efTheta += FP_360_0;
  556. break;
  557. }
  558. }
  559. ASSERTGDI(!efTheta.bIsNegative() && efTheta <= FP_360_0,
  560. "Arctan: Weird result\n");
  561. lQuadrant = (LONG) gajArctanQuadrant[lOctant];
  562. return;
  563. }
  564. /******************************Public*Routine******************************\
  565. * EFLOAT efSin(efTheta)
  566. *
  567. * Returns the Sine of efTheta, which is specified in degrees. Uses
  568. * a look-up table with linear interpolation for the approximation.
  569. * It is accurate to within 0.02% using a table size of 32.
  570. *
  571. * History:
  572. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  573. * Wrote it.
  574. \**************************************************************************/
  575. EFLOAT efSin(EFLOAT efTheta)
  576. {
  577. BOOL bNegate = FALSE;
  578. EFLOATEXT efResult;
  579. // Use property that Sin(-x) = -Sin(x):
  580. if (efTheta.bIsNegative())
  581. {
  582. bNegate = TRUE;
  583. efTheta.vNegate();
  584. }
  585. // efIndex = (efTheta / 90) * SINE_TABLE_SIZE:
  586. EFLOAT efIndex = efTheta;
  587. efIndex *= FP_SINE_FACTOR;
  588. // Use floor of efIndex to compute table index:
  589. LONG lIndex;
  590. efIndex.bEfToLTruncate(lIndex);
  591. // efDelta is used for the linear interpolation:
  592. EFLOAT efDelta;
  593. efIndex.vFraction(efDelta);
  594. // Compute the quadrant (0 to 3) in which the angle is:
  595. LONG lQuadrant = lIndex >> SINE_TABLE_POWER;
  596. // Use property that Sin(180 + x) = -Sin(x):
  597. if (lQuadrant & 2)
  598. bNegate = !bNegate;
  599. if (lQuadrant & 1)
  600. {
  601. // Use property that Sin(90 + x) = Sin(90 - x):
  602. lIndex = SINE_TABLE_SIZE - (lIndex & SINE_TABLE_MASK);
  603. // efResult = gaefSin[lIndex]
  604. // - efDelta * (gaefSin[lIndex] - gaefSin[lIndex - 1]):
  605. efResult = gaefSin[lIndex];
  606. efResult -= gaefSin[lIndex - 1];
  607. efResult *= efDelta;
  608. efResult.vNegate();
  609. efResult += gaefSin[lIndex];
  610. }
  611. else
  612. {
  613. lIndex &= SINE_TABLE_MASK;
  614. // efResult = gaefSin[lIndex]
  615. // + efDelta * (gaefSin[lIndex + 1] - gaefSin[lIndex]):
  616. efResult = gaefSin[lIndex + 1];
  617. efResult -= gaefSin[lIndex];
  618. efResult *= efDelta;
  619. efResult += gaefSin[lIndex];
  620. }
  621. if (bNegate)
  622. efResult.vNegate();
  623. return (efResult);
  624. }
  625. /******************************Public*Routine******************************\
  626. * EFLOAT efCos(efTheta)
  627. *
  628. * Returns the Cosine of efTheta, which is specified in degrees.
  629. *
  630. * Note: Because of rounding errors, for very large values of efTheta,
  631. * it's possible that the value returned from efCos(efTheta) is
  632. * approximately that returned from efSin(efTheta).
  633. *
  634. * History:
  635. * 19-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  636. * Wrote it.
  637. \**************************************************************************/
  638. EFLOAT efCos(EFLOAT efTheta)
  639. {
  640. efTheta += FP_90_0;
  641. return(efSin(efTheta));
  642. }
  643. /******************************Public*Routine******************************\
  644. * VOID vCosSin(efTheta, pefCos, pefSin)
  645. *
  646. * Returns the Cosine and Sine of efTheta, which is specified in degrees.
  647. * Uses a look-up table with linear interpolation for the approximation.
  648. * It is accurate to within 0.02% using a table size of 32.
  649. *
  650. * Unlike separately calling efCos(efTheta) and efSin(efTheta), this function
  651. * will at least guarantee that the returned point is on the unit circle.
  652. * However, for APIs such as Arc() and AngleArc(), the error in the computation
  653. * will be far enough off the unit circle so that those APIs don't function
  654. * correctly. In those cases where even a small error can be significant, one
  655. * should use vCosSinPrecise() to get an exact computation (to within the precision
  656. * of a float) of sine and cosine.
  657. *
  658. * History:
  659. * 5-May-1993 -by- J. Andrew Goossen [andrewgo]
  660. * Wrote it.
  661. \**************************************************************************/
  662. VOID vCosSin(EFLOAT efTheta, EFLOAT* pefCos, EFLOAT* pefSin)
  663. {
  664. BOOL bNegate = FALSE;
  665. EFLOATEXT efResult;
  666. LONG lTmpIndex;
  667. // ------------------------------------------------------------------
  668. // Handle setup common to both Sin and Cos.
  669. // Use property that Sin(-x) = -Sin(x):
  670. if (efTheta.bIsNegative())
  671. {
  672. bNegate = TRUE;
  673. efTheta.vNegate();
  674. }
  675. // efIndex = (efTheta / 90) * SINE_TABLE_SIZE:
  676. EFLOAT efIndex = efTheta;
  677. efIndex *= FP_SINE_FACTOR;
  678. // Use floor of efIndex to compute table index:
  679. LONG lIndex;
  680. efIndex.bEfToLTruncate(lIndex);
  681. // efDelta is used for the linear interpolation:
  682. EFLOAT efDelta;
  683. efIndex.vFraction(efDelta);
  684. // Compute the quadrant (0 to 3) in which the angle is:
  685. LONG lQuadrant = lIndex >> SINE_TABLE_POWER;
  686. // ------------------------------------------------------------------
  687. // Now handle Sin(efTheta).
  688. // Use property that Sin(180 + x) = -Sin(x):
  689. if (lQuadrant & 2)
  690. bNegate = !bNegate;
  691. if (lQuadrant & 1)
  692. {
  693. // Use property that Sin(90 + x) = Sin(90 - x):
  694. lTmpIndex = SINE_TABLE_SIZE - (lIndex & SINE_TABLE_MASK);
  695. // efResult = gaefSin[lTmpIndex]
  696. // - efDelta * (gaefSin[lTmpIndex] - gaefSin[lTmpIndex - 1]):
  697. efResult = gaefSin[lTmpIndex];
  698. efResult -= gaefSin[lTmpIndex - 1];
  699. efResult *= efDelta;
  700. efResult.vNegate();
  701. efResult += gaefSin[lTmpIndex];
  702. }
  703. else
  704. {
  705. lIndex &= SINE_TABLE_MASK;
  706. // efResult = gaefSin[lIndex]
  707. // + efDelta * (gaefSin[lIndex + 1] - gaefSin[lIndex]):
  708. efResult = gaefSin[lIndex + 1];
  709. efResult -= gaefSin[lIndex];
  710. efResult *= efDelta;
  711. efResult += gaefSin[lIndex];
  712. }
  713. if (bNegate)
  714. efResult.vNegate();
  715. *pefSin = efResult;
  716. // ------------------------------------------------------------------
  717. // Now handle Cos(efTheta).
  718. // Since Cos(-x) = Cos(x), bNegate is always initially false:
  719. bNegate = FALSE;
  720. // We use the property that Cos(x) = Sin(x + 90) to convert the
  721. // problem to determining the Sine again:
  722. lQuadrant++;
  723. // Use property that Sin(180 + x) = -Sin(x):
  724. if (lQuadrant & 2)
  725. bNegate = !bNegate;
  726. if (lQuadrant & 1)
  727. {
  728. // Use property that Sin(90 + x) = Sin(90 - x):
  729. lTmpIndex = SINE_TABLE_SIZE - (lIndex & SINE_TABLE_MASK);
  730. // efResult = gaefSin[lTmpIndex]
  731. // - efDelta * (gaefSin[lTmpIndex] - gaefSin[lTmpIndex - 1]):
  732. efResult = gaefSin[lTmpIndex];
  733. efResult -= gaefSin[lTmpIndex - 1];
  734. efResult *= efDelta;
  735. efResult.vNegate();
  736. efResult += gaefSin[lTmpIndex];
  737. }
  738. else
  739. {
  740. lIndex &= SINE_TABLE_MASK;
  741. // efResult = gaefSin[lIndex]
  742. // + efDelta * (gaefSin[lIndex + 1] - gaefSin[lIndex]):
  743. efResult = gaefSin[lIndex + 1];
  744. efResult -= gaefSin[lIndex];
  745. efResult *= efDelta;
  746. efResult += gaefSin[lIndex];
  747. }
  748. if (bNegate)
  749. efResult.vNegate();
  750. *pefCos = efResult;
  751. }
  752. /******************************Public*Routine******************************\
  753. * VOID vCosSinPrecise(efTheta, pefCos, pefSin)
  754. *
  755. * Returns the Cosine and Sine of efTheta, which is specified in degrees.
  756. * This function should be used when more precision is needed than vCosSin
  757. * can provide.
  758. *
  759. * This function uses the Taylor (actually MacLaurin) expansion of
  760. * sin x and cos x out to (NUM_TERMS / 2) terms, where x is in radians.
  761. * The MacLaurin expansions are:
  762. *
  763. * cos x = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! + ... + (-1)^n (x^(2n))/(2n)! + ...
  764. * sin x = x - (x^3)/3! + (x^5)/5! - (x^7)/7! + ... + (-1)^n (x^(2n+1))/(2n+1)! + ...
  765. *
  766. * The result will have error no more than the next term that would have been computed
  767. * (since it is an alternating and converging series.
  768. * If x is between 0 and pi/2, then if NUM_TERMS = 13, the error (assuming the
  769. * floating point compuations are exact) is no more than 5.7e-08, which is less than
  770. * the precision of an IEEE floating point number (23 bits of precision).
  771. *
  772. * History:
  773. * 19-Feb-1999 -by- Donald Chinn [dchinn]
  774. * Wrote it.
  775. \**************************************************************************/
  776. #define NUM_TERMS 13
  777. VOID vCosSinPrecise(EFLOAT efTheta, EFLOAT* pefCos, EFLOAT* pefSin)
  778. {
  779. EFLOAT efTemp, efTemp2; // temporaries for intermediate calculation
  780. BOOL bThetaIsNegative = FALSE;
  781. BOOL bThetaIsGr180 = FALSE;
  782. BOOL bThetaIsGr90 = FALSE;
  783. ULONG i;
  784. EFLOAT efThetaRadians; // theta in radians
  785. EFLOAT efCosResult;
  786. EFLOAT efSinResult;
  787. EFLOAT efI; // used to hold the floating point value of i during expansion
  788. EFLOAT efThetaRadiansPow; // used to hold (efThetaRadians)^i during expansion
  789. EFLOAT efFactorial; // used to hold i! during expansion
  790. EFLOAT efTerm; // used to hold +/- (efThetaRadians)^i / (i!)
  791. // sin(x) = - sin(-x)
  792. // cos(x) = cos(-x)
  793. if (efTheta.bIsNegative())
  794. {
  795. bThetaIsNegative = TRUE;
  796. efTheta.vNegate();
  797. }
  798. // ASSERT: efTheta >= 0
  799. // sin(x) = sin(x + 360*i), for all integers i. (x in degrees)
  800. // cos(x) = cos(x + 360*i), for all integers i.
  801. efTemp = efTheta;
  802. efTemp /= FP_360_0;
  803. efTemp.vFraction(efTemp2);
  804. efTheta = efTemp2;
  805. efTheta *= FP_360_0;
  806. // ASSERT: 0 <= efTheta < 360
  807. // if 0 <= x <= 360 (x in degrees), then
  808. // sin(x) = - sin(360 - x)
  809. // cos(x) = cos(360 - x).
  810. // if efTheta > 180, then set efTheta = 360 - efTheta.
  811. efTemp = FP_180_0;
  812. efTemp -= efTheta;
  813. if (efTemp.bIsNegative())
  814. {
  815. bThetaIsGr180 = TRUE;
  816. efTemp = FP_360_0;
  817. efTemp -= efTheta;
  818. efTheta = efTemp;
  819. }
  820. // ASSERT: 0 <= efTheta <= 180
  821. // if 0 <= x <= 180 (x in degrees), then
  822. // sin(x) = sin(180 - x)
  823. // cos(x) = - cos(180 - x).
  824. // if efTheta > 90, then set efTheta = 180 - efTheta.
  825. efTemp = FP_90_0;
  826. efTemp -= efTheta;
  827. if (efTemp.bIsNegative())
  828. {
  829. bThetaIsGr90 = TRUE;
  830. efTemp = FP_180_0;
  831. efTemp -= efTheta;
  832. efTheta = efTemp;
  833. }
  834. // ASSERT: 0 <= efTheta <= 90
  835. // convert input angle from degrees to radians
  836. // efThetaRadians = efTheta * PI / 180;
  837. efThetaRadians = efTheta;
  838. efThetaRadians *= FP_PI;
  839. efThetaRadians /= FP_180_0;
  840. // first term of the MacLaurin expansion
  841. efCosResult = FP_1_0;
  842. efSinResult = efThetaRadians;
  843. // later terms in the MacLaurin expansion
  844. // in this loop, i corresponds to the term +/- (efThetaRadians)^i/(i!)
  845. for (i = 2, efI = FP_2_0, efFactorial = FP_2_0, efThetaRadiansPow = efThetaRadians;
  846. i < NUM_TERMS;
  847. i++, efI += FP_1_0, efFactorial *= efI)
  848. {
  849. // compute (efThetaRadians)^i
  850. efThetaRadiansPow *= efThetaRadians;
  851. // ASSERT: at this point, i, efI, efThetaRadiansPow, and efFactorial are all consistent
  852. // compute the i-th term
  853. efTerm = efThetaRadiansPow;
  854. efTerm /= efFactorial;
  855. // sign of the term -- the pattern is:
  856. // if i == 2 or 3, then the sign is negative;
  857. // if i == 4 or 5, then the sign is positive;
  858. // etc. (the pattern repeats every two values of i)
  859. if ((i / 2) % 2)
  860. {
  861. // i/2 is odd -- sign is negative
  862. efTerm.vNegate();
  863. }
  864. // add the term to the appropriate expansion
  865. if (i % 2)
  866. {
  867. // i is odd -- add to the sine expansion
  868. efSinResult += efTerm;
  869. }
  870. else
  871. {
  872. // i is even -- add to the cosine expansion
  873. efCosResult += efTerm;
  874. }
  875. }
  876. // Adjust the sign of the result
  877. if ((bThetaIsNegative && !bThetaIsGr180) ||
  878. (!bThetaIsNegative && bThetaIsGr180))
  879. {
  880. efSinResult.vNegate();
  881. }
  882. if (bThetaIsGr90)
  883. {
  884. efCosResult.vNegate();
  885. }
  886. *pefCos = efCosResult;
  887. *pefSin = efSinResult;
  888. }
  889. /******************************Public*Routine******************************\
  890. * BOOL bPartialQuadrantArc(paType, epo, ebox, efStartAngle, efEndAngle)
  891. *
  892. * Constructs a partial arc of 90 degrees or less using an approximation
  893. * technique by Kirk Olynyk. The arc is approximated by a cubic Bezier.
  894. * Optionally draws a line to the first point.
  895. *
  896. * Restrictions:
  897. *
  898. * efEndAngle must be within 90 degrees of efStartAngle.
  899. *
  900. * Steps in constructing the curve:
  901. *
  902. * 1) Construct the conic section at the origin for the unit circle;
  903. * 2) Approximate this conic by a cubic Bezier;
  904. * 3) Scale and translate result.
  905. *
  906. * 1) Constructing the Conic
  907. *
  908. * 'efStartAngle' and 'efEndAngle' determine the end-points of the
  909. * conic (call them vectors from the origin, A and C). We need the
  910. * middle vector B and the sharpness to completely determine the
  911. * conic.
  912. *
  913. * For the portion of a circular arc that is 90 degrees or less,
  914. * conic sharpness is Cos((efEndAngle - efStartAngle) / 2).
  915. *
  916. * B is calculated by the intersection of the two lines that are
  917. * at the ends of A and C and are perpendicular to A and C,
  918. * respectively. That is, since A and C lie on the unit circle, B
  919. * is the point of intersection of the two lines that are tangent
  920. * to the unit circle at A and C.
  921. *
  922. * If A = (a, b), then the equation of the line through (a, b)
  923. * tangent to the circle is ax + by = 1. Similarly, for
  924. * C = (c, d), the equation of the line is cx + dy = 1. The
  925. * intersection of these two lines is defined by:
  926. *
  927. * x = (d - b) / (ad - bc)
  928. * and y = (a - c) / (ad - bc).
  929. *
  930. * Then, B = (x, y).
  931. *
  932. * 2) Approximating the conic as a Bezier cubic
  933. *
  934. * For sharpness values 'close' to 1, the conic may be approximated
  935. * by a cubic Bezier; error is less for sharpnesses closer to 1.
  936. *
  937. * Error
  938. *
  939. * Since the largest angle handled by this routine is 90 degrees,
  940. * sharpness is guaranteed to be between 1 / sqrt(2) = .707 and 1.
  941. * Error in the approximation for a 90 degree arc is approximately
  942. * 0.2%; it is less for smaller angles. 0.2% is deemed small
  943. * enough error; thus, a 90 degree circular arc is always
  944. * approximated by just one Bezier.
  945. *
  946. * One notable implication of the fact that arcs have less error
  947. * for smaller angles is that when a partial arc is xor-ed with
  948. * the corresponding complete ellipse, some of the partial arc
  949. * will not be completely xor-ed out. (Too bad.)
  950. *
  951. * Given a conic section defined by (A, B, C, S), we find the
  952. * cubic Bezier defined by the four control points (V0, V1, V2, V3)
  953. * that provides the closest approimxation. We require that the
  954. * Bezier be tangent to the triangle at the same endpoints. That is,
  955. *
  956. * V1 = (1 - Tau1) A + (Tau1) B
  957. * V2 = (1 - Tau2) C + (Tau2) B
  958. *
  959. * Simplify by taking Tau = Tau1 = Tau2, and we get:
  960. *
  961. * V0 = A
  962. * V1 = (1 - Tau) A + (Tau) B
  963. * V2 = (1 - Tau) C + (Tau) B
  964. * V3 = C
  965. *
  966. *
  967. * Where Tau = 4 S / (3 (S + 1)), S being the sharpness.
  968. * S = cos(angle / 2) for an arc of 90 degrees or less.
  969. * So, for one quadrant of a circle, and since A and B actually
  970. * extend from the corners of the bound box, and not the center,
  971. *
  972. * Tau = 1 - (4 * cos(45)) / (3 * (cos(45) + 1)) = 0.44772...
  973. *
  974. * See Kirk Olynyk's "Conics to Beziers" for more.
  975. *
  976. * 3) The arc is transformed to the bound box.
  977. *
  978. * History:
  979. * 27-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  980. * Wrote it.
  981. \**************************************************************************/
  982. BOOL bPartialQuadrantArc
  983. (
  984. PARTIALARC paType, // MoveToEx or LineTo the first point
  985. EPATHOBJ& epo,
  986. EBOX& ebox, // Bound box
  987. EPOINTFL& eptefVecA,
  988. EFLOAT& efStartAngle,
  989. EPOINTFL& eptefVecC,
  990. EFLOAT& efEndAngle
  991. )
  992. {
  993. EPOINTFL ptefV0;
  994. EPOINTFL ptefV1;
  995. EPOINTFL ptefV2;
  996. EPOINTFL ptefV3;
  997. EPOINTFL eptefVecB;
  998. // Do some explicit common sub-expression elimination:
  999. // efDenom = eptefVecA.x * eptefVecC.y - eptefVecA.y * eptefVecC.x;
  1000. EFLOAT efTerm2 = eptefVecA.y;
  1001. efTerm2 *= eptefVecC.x;
  1002. EFLOAT efDenom = eptefVecA.x;
  1003. efDenom *= eptefVecC.y;
  1004. efDenom -= efTerm2;
  1005. efDenom.vAbs();
  1006. // efDenom == 0 if eptefVecA and eptefVecC are parallel vectors.
  1007. // Since they're both centered at the origin, and are in the
  1008. // same quadrant, this implies that eptefVecA == eptefVecC, or
  1009. // equivalently, efStartAngle == efEndAngle, which we special case.
  1010. //
  1011. // Compare to epsilon, which is arbitrarily 2 ^ -16, thus skipping
  1012. // angles of less than 0.000874 degrees.
  1013. if (efDenom <= FP_EPSILON)
  1014. {
  1015. // We have a zero degree arc. If we're doing a _LINETO or _MOVETO,
  1016. // we have to set the current point in the path. We can't early
  1017. // out if doing a _CONTINUE because of AngleArc in XOR mode doing
  1018. // a sweep of more than 360 degrees when the start angle is a
  1019. // multiple of 90 degrees: bPartialQuadrantArc may have left the
  1020. // current position in a slightly different point what we will
  1021. // expect for the next part of the arc, due to rounding error.
  1022. ptefV0 = eptefVecA;
  1023. ptefV1 = ptefV0;
  1024. ptefV2 = eptefVecC;
  1025. ptefV3 = ptefV2;
  1026. }
  1027. else
  1028. {
  1029. // eptefVecB.x = (eptefVecC.y - eptefVecA.y) / efDenom;
  1030. // eptefVecB.y = (eptefVecA.x - eptefVecC.x) / efDenom;
  1031. eptefVecB.x = eptefVecC.y;
  1032. eptefVecB.x -= eptefVecA.y;
  1033. eptefVecB.x /= efDenom;
  1034. eptefVecB.y = eptefVecA.x;
  1035. eptefVecB.y -= eptefVecC.x;
  1036. eptefVecB.y /= efDenom;
  1037. // efSharp = efCos((efEndAngle - efStartAngle) / 2.0f):
  1038. EFLOAT efSharp;
  1039. {
  1040. EFLOAT efSweep = efEndAngle;
  1041. efSweep -= efStartAngle;
  1042. efSweep.vDivBy2();
  1043. efSharp = efCos(efSweep);
  1044. // Given cos(x + n * 180) = +/- cos(x) for integer n. We
  1045. // know that the arc is 90 degrees or less, so efSharp is
  1046. // non-negative. This lets us effectively use input angles
  1047. // modulo 360 degrees:
  1048. efSharp.vAbs();
  1049. }
  1050. // At this point we've figured out the control points and sharpness
  1051. // of the conic section defining the arc. Now convert them to Bezier
  1052. // form.
  1053. {
  1054. EFLOAT efSharpPlusOne = efSharp;
  1055. efSharpPlusOne += FP_1_0;
  1056. // efAlpha = Tau = 4 * S / (3 * (S + 1)):
  1057. EFLOAT efAlpha = FP_4DIV3;
  1058. efAlpha *= efSharp;
  1059. efAlpha /= efSharpPlusOne;
  1060. // efBeta = 1 - Tau:
  1061. EFLOAT efBeta = FP_1_0;
  1062. efBeta -= efAlpha;
  1063. // ptefAlphaTimesVecB = (Tau) B:
  1064. EPOINTFL ptefAlphaTimesVecB = eptefVecB;
  1065. ptefAlphaTimesVecB *= efAlpha;
  1066. // V0 = A:
  1067. ptefV0 = eptefVecA;
  1068. // V1 = (1 - Tau) A + (Tau) B:
  1069. ptefV1 = eptefVecA;
  1070. ptefV1 *= efBeta;
  1071. ptefV1 += ptefAlphaTimesVecB;
  1072. // V2 = (1 - Tau) C + (Tau) B:
  1073. ptefV2 = eptefVecC;
  1074. ptefV2 *= efBeta;
  1075. ptefV2 += ptefAlphaTimesVecB;
  1076. // V3 = C:
  1077. ptefV3 = eptefVecC;
  1078. }
  1079. }
  1080. // When PARTIALARCTYPE_CONTINUE is set, we know that the first control
  1081. // point of the Bezier is the same as the last point added to the path,
  1082. // so we don't have to add it again.
  1083. if (paType != PARTIALARCTYPE_CONTINUE)
  1084. {
  1085. POINTL ptl = ebox.ptlXform(ptefV0);
  1086. switch (paType)
  1087. {
  1088. case PARTIALARCTYPE_MOVETO:
  1089. if (!epo.bMoveTo((PEXFORMOBJ) NULL, &ptl))
  1090. return(FALSE);
  1091. break;
  1092. case PARTIALARCTYPE_LINETO:
  1093. if (!epo.bPolyLineTo((PEXFORMOBJ) NULL, &ptl, 1))
  1094. return(FALSE);
  1095. break;
  1096. }
  1097. }
  1098. POINTL aptl[3];
  1099. // Now transform to the bound box:
  1100. aptl[0] = ebox.ptlXform(ptefV1);
  1101. aptl[1] = ebox.ptlXform(ptefV2);
  1102. aptl[2] = ebox.ptlXform(ptefV3);
  1103. return(epo.bPolyBezierTo((PEXFORMOBJ) NULL, aptl, 3));
  1104. }
  1105. /******************************Public*Routine******************************\
  1106. * VOID vGetAxis(lQuadrant, eptef)
  1107. *
  1108. * History:
  1109. * 22-Aug-1991 -by- J. Andrew Goossen [andrewgo]
  1110. * Wrote it.
  1111. \**************************************************************************/
  1112. inline VOID vGetAxis
  1113. (
  1114. LONG lQuadrant,
  1115. EPOINTFL& eptef
  1116. )
  1117. {
  1118. eptef.x = gaefAxisCoord[(lQuadrant + 1) & 3];
  1119. eptef.y = gaefAxisCoord[lQuadrant];
  1120. }
  1121. /******************************Public*Routine******************************\
  1122. * BOOL bPartialArc(paType, epo, ebox,
  1123. * eptefStart, lStartQuadrant, efStartAngle,
  1124. * eptefEnd, lEndQuadrant, efEndAngle,
  1125. * lQuadrants)
  1126. *
  1127. * Constructs a partial arc. Optionally draws a line to the first point.
  1128. * The arc is drawn counter-clockwise. efStartAngle and efEndAngle are
  1129. * interpretted modulo 360. If the start and end are coincident, a
  1130. * complete ellipse is drawn if lQuadrants != 0.
  1131. *
  1132. * It works by breaking the arc into curves of 90 degrees or less, and
  1133. * then approximating these using Beziers.
  1134. *
  1135. * Restrictions: Only draws counter-clockwise, up to one revolution.
  1136. *
  1137. * History:
  1138. * 27-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  1139. * Wrote it.
  1140. \**************************************************************************/
  1141. BOOL bPartialArc
  1142. (
  1143. PARTIALARC paType,
  1144. EPATHOBJ& epo,
  1145. EBOX& ebox,
  1146. EPOINTFL& eptefStart,
  1147. LONG lStartQuadrant,
  1148. EFLOAT& efStartAngle,
  1149. EPOINTFL& eptefEnd,
  1150. LONG lEndQuadrant,
  1151. EFLOAT& efEndAngle,
  1152. LONG lQuadrants
  1153. )
  1154. {
  1155. BOOL bSuccess;
  1156. // If arc is less than 90 degrees, we can make the call straight to
  1157. // bPartialQuadrantArc:
  1158. if (lQuadrants == 0)
  1159. {
  1160. bSuccess = bPartialQuadrantArc(paType,
  1161. epo,
  1162. ebox,
  1163. eptefStart,
  1164. efStartAngle,
  1165. eptefEnd,
  1166. efEndAngle);
  1167. }
  1168. else
  1169. {
  1170. // Increment lStartQuadrant so that it's actually the quadrant
  1171. // of the first possible 90 degree arc:
  1172. lStartQuadrant = (lStartQuadrant + 1) & 3;
  1173. // The arc is more than 90 degrees, so we have to break it
  1174. // up into chunks that are 90 degrees or smaller. We break it
  1175. // up by quadrant.
  1176. EAPOINTL aeptl[3]; // Buffer for quadrant arcs
  1177. EPOINTFL eptefAxis;
  1178. vGetAxis(lStartQuadrant, eptefAxis);
  1179. bSuccess = bPartialQuadrantArc(paType,
  1180. epo,
  1181. ebox,
  1182. eptefStart,
  1183. efStartAngle,
  1184. eptefAxis,
  1185. gaefAxisAngle[lStartQuadrant]);
  1186. if (lStartQuadrant != lEndQuadrant)
  1187. {
  1188. // Compute vectors for constructing arcs of exactly 90 degrees:
  1189. EPOINTL eptlC;
  1190. EPOINTL eptlD;
  1191. LONGLONG eq; // Temporary variable
  1192. // Compute the placement of the inner control points for the
  1193. // Bezier curve:
  1194. vEllipseControlsIn((VECTORFX*) &ebox.eptlA, (VECTORFX*) &eptlC, &eq);
  1195. vEllipseControlsIn((VECTORFX*) &ebox.eptlB, (VECTORFX*) &eptlD, &eq);
  1196. LONG ll = lStartQuadrant;
  1197. do
  1198. {
  1199. switch (ll)
  1200. {
  1201. case 0:
  1202. aeptl[0] = ebox.aeptl[0]; aeptl[0] -= eptlD;
  1203. aeptl[1] = ebox.aeptl[0]; aeptl[1] -= eptlC;
  1204. aeptl[2] = ebox.aeptl[0]; aeptl[2] -= ebox.eptlA;
  1205. break;
  1206. case 1:
  1207. aeptl[0] = ebox.aeptl[1]; aeptl[0] += eptlC;
  1208. aeptl[1] = ebox.aeptl[1]; aeptl[1] -= eptlD;
  1209. aeptl[2] = ebox.aeptl[1]; aeptl[2] -= ebox.eptlB;
  1210. break;
  1211. case 2:
  1212. aeptl[0] = ebox.aeptl[2]; aeptl[0] += eptlD;
  1213. aeptl[1] = ebox.aeptl[2]; aeptl[1] += eptlC;
  1214. aeptl[2] = ebox.aeptl[2]; aeptl[2] += ebox.eptlA;
  1215. break;
  1216. case 3:
  1217. aeptl[0] = ebox.aeptl[3]; aeptl[0] -= eptlC;
  1218. aeptl[1] = ebox.aeptl[3]; aeptl[1] += eptlD;
  1219. aeptl[2] = ebox.aeptl[3]; aeptl[2] += ebox.eptlB;
  1220. break;
  1221. }
  1222. bSuccess &= epo.bPolyBezierTo((PEXFORMOBJ) NULL, aeptl, 3);
  1223. ll = (ll + 1) & 3;
  1224. } while (ll != lEndQuadrant);
  1225. }
  1226. vGetAxis(lEndQuadrant, eptefAxis);
  1227. bSuccess &= bPartialQuadrantArc(PARTIALARCTYPE_CONTINUE,
  1228. epo,
  1229. ebox,
  1230. eptefAxis,
  1231. gaefAxisAngle[lEndQuadrant],
  1232. eptefEnd,
  1233. efEndAngle);
  1234. }
  1235. return(bSuccess);
  1236. }
  1237. /******************************Public*Routine******************************\
  1238. * BOOL NtGdiArcInternal (arctype,x1,y1,x2,y2,x3,y3,x4,y4)
  1239. *
  1240. * Draws an arc figure. Used by the 'Arc', 'Chord' and 'Pie' APIs.
  1241. * Adds to the active path associated with the DC if there is one;
  1242. * otherwise, adds to a temporary one and bStroke's or bStrokeAndFill's
  1243. * it.
  1244. *
  1245. * History:
  1246. * 27-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  1247. * Wrote it.
  1248. \**************************************************************************/
  1249. BOOL
  1250. APIENTRY
  1251. NtGdiArcInternal(
  1252. ARCTYPE arctype, // Arc, Pie, Chord, or ArcTo
  1253. HDC hdc,
  1254. int x1,
  1255. int y1,
  1256. int x2,
  1257. int y2,
  1258. int x3,
  1259. int y3,
  1260. int x4,
  1261. int y4
  1262. )
  1263. {
  1264. ERECTL ercl(x1, y1, x2, y2);
  1265. EPOINTL ptl1(x3, y3);
  1266. EPOINTL ptl2(x4, y4);
  1267. DCOBJ dco(hdc);
  1268. if (!dco.bValid())
  1269. {
  1270. SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
  1271. return(FALSE);
  1272. }
  1273. // Watch out: enums are signed, so we need to check "both ends":
  1274. if ((arctype < 0) || (arctype >= ARCTYPE_MAX))
  1275. {
  1276. RIP("bCurve: Unknown curve type.");
  1277. SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
  1278. return(FALSE);
  1279. }
  1280. SYNC_DRAWING_ATTRS(dco.pdc);
  1281. // Get the current path or a temporary path. If we're doing an ArcTo,
  1282. // notify that we will update the current point:
  1283. PATHSTACKOBJ pso(dco, arctype == ARCTYPE_ARCTO);
  1284. if (!pso.bValid())
  1285. {
  1286. SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
  1287. return(FALSE);
  1288. }
  1289. // Need World-to-Device transform for widening the line:
  1290. EXFORMOBJ exo(dco, WORLD_TO_DEVICE);
  1291. LINEATTRS *pla = dco.plaRealize(exo);
  1292. // Handle the PS_INSIDEFRAME pen attribute and lower-right exclusion
  1293. // by adjusting the box now. At the same time, get the transform
  1294. // type and order the rectangle. Plus, pass TRUE to indicate that
  1295. // the bound box should be adjusted for nice output with NULL pens:
  1296. EBOX ebox(dco, ercl, pla, TRUE);
  1297. // Like Win3, we don't handle the case when the pen is bigger than either
  1298. // dimension of the bound-box:
  1299. if (ebox.bFillInsideFrame())
  1300. return(FALSE);
  1301. // We exit early if the the bound box is less than a pixel in some
  1302. // dimension and we're doing lower-right exclusion. Since we have
  1303. // drawn what the app asked for, we return success.
  1304. if (ebox.bEmpty())
  1305. {
  1306. return(TRUE);
  1307. }
  1308. EPOINTFL eptefOrg(efMid(ebox.rclWorld.left, ebox.rclWorld.right),
  1309. efMid(ebox.rclWorld.top, ebox.rclWorld.bottom));
  1310. EFLOAT efStartAngle;
  1311. EFLOAT efEndAngle;
  1312. LONG lStartQuad;
  1313. LONG lEndQuad;
  1314. // Make sure we don't divide by zero:
  1315. if (ebox.rclWorld.left == ebox.rclWorld.right ||
  1316. ebox.rclWorld.top == ebox.rclWorld.bottom)
  1317. {
  1318. efEndAngle = efStartAngle = FP_0_0;
  1319. lStartQuad = lEndQuad = 0;
  1320. }
  1321. else
  1322. {
  1323. EFLOAT efdx = efHalfDiff(ebox.rclWorld.right, ebox.rclWorld.left);
  1324. EFLOAT efdy = efHalfDiff(ebox.rclWorld.top, ebox.rclWorld.bottom);
  1325. ASSERTGDI(!efdx.bIsZero() && !efdy.bIsZero(), "bArc: scaling is zero\n");
  1326. // Compute the start and end angles of the ellipse when its
  1327. // more convenient, namely when we have the points in world
  1328. // space.
  1329. //
  1330. // The ellipse must be scaled back to the unit circle before
  1331. // the angle is computed. The signs of 'efdx' and 'efdy'
  1332. // determine the correct orientation:
  1333. EPOINTFL ptefNormalized;
  1334. ptefNormalized = ptl1;
  1335. ptefNormalized -= eptefOrg;
  1336. ptefNormalized.x /= efdx;
  1337. ptefNormalized.y /= efdy;
  1338. vArctan(ptefNormalized.x, ptefNormalized.y, efStartAngle, lStartQuad);
  1339. ptefNormalized = ptl2;
  1340. ptefNormalized -= eptefOrg;
  1341. ptefNormalized.x /= efdx;
  1342. ptefNormalized.y /= efdy;
  1343. vArctan(ptefNormalized.x, ptefNormalized.y, efEndAngle, lEndQuad);
  1344. // If efEndAngle == efStartAngle, we'll draw a complete ellipse.
  1345. // Note that we can't just call 'bEllipse' for this case because
  1346. // drawing must start and end at the specified angles (this
  1347. // matters for styled lines).
  1348. }
  1349. EPOINTFL eptefStart;
  1350. EPOINTFL eptefEnd;
  1351. EFLOAT efAngleSwept;
  1352. BOOL bAngleSweptIsZero;
  1353. // If the difference between efEndAngle and efStartAngle is less than about 3 degrees,
  1354. // then the error in computation of eptefStart and eptefEnd using vCosSin will be enough
  1355. // so that the computation of the Bezier points in bPartialArc (which calls bPartialQuadrantArc)
  1356. // will be noticeably wrong.
  1357. // if efEndAngle == efStartAngle, then we are drawing an entire ellipse, and so
  1358. // it is safe to use vCosSin.
  1359. // determine whether (abs(efEndAngle - efStartAngle) - 3.0 < 0.0)
  1360. efAngleSwept = efEndAngle;
  1361. efAngleSwept -= efStartAngle;
  1362. if (efAngleSwept.bIsNegative())
  1363. {
  1364. efAngleSwept.vNegate();
  1365. }
  1366. bAngleSweptIsZero = efAngleSwept.bIsZero();
  1367. efAngleSwept -= FP_3_0;
  1368. if (efAngleSwept.bIsNegative() && !bAngleSweptIsZero)
  1369. {
  1370. vCosSinPrecise(efStartAngle, &eptefStart.x, &eptefStart.y);
  1371. vCosSinPrecise(efEndAngle, &eptefEnd.x, &eptefEnd.y);
  1372. }
  1373. else
  1374. {
  1375. vCosSin(efStartAngle, &eptefStart.x, &eptefStart.y);
  1376. vCosSin(efEndAngle, &eptefEnd.x, &eptefEnd.y);
  1377. }
  1378. if (!bPartialArc((arctype == ARCTYPE_ARCTO) ?
  1379. PARTIALARCTYPE_LINETO :
  1380. PARTIALARCTYPE_MOVETO,
  1381. pso,
  1382. ebox,
  1383. eptefStart,
  1384. lStartQuad,
  1385. efStartAngle,
  1386. eptefEnd,
  1387. lEndQuad,
  1388. efEndAngle,
  1389. ((lStartQuad == lEndQuad) &&
  1390. (efEndAngle > efStartAngle)) ? 0 : 1))
  1391. return(FALSE);
  1392. switch(arctype)
  1393. {
  1394. case ARCTYPE_ARC:
  1395. break;
  1396. case ARCTYPE_ARCTO:
  1397. // Set the DC's current position in device space. It would be too much
  1398. // work to calculate the world space current position, so simply mark it
  1399. // as invalid:
  1400. dco.pdc->vInvalidatePtlCurrent();
  1401. dco.pdc->vValidatePtfxCurrent();
  1402. dco.ptfxCurrent() = pso.ptfxGetCurrent();
  1403. break;
  1404. case ARCTYPE_CHORD:
  1405. // Draw a line from the end of the curve to the start to create
  1406. // the 'Chord':
  1407. if (!pso.bCloseFigure())
  1408. return(FALSE);
  1409. break;
  1410. case ARCTYPE_PIE:
  1411. {
  1412. // Draw a line from the end of the curve to the center of the
  1413. // figure to the start of the curve to create the 'Pie':
  1414. if (!pso.bPolyLineTo((PEXFORMOBJ) NULL, &ebox.eptlOrigin, 1) ||
  1415. !pso.bCloseFigure())
  1416. return(FALSE);
  1417. }
  1418. break;
  1419. }
  1420. // Return if we're accumulating a path:
  1421. if (dco.pdc->bActive())
  1422. return(TRUE);
  1423. // Stroke or StrokeAndFill depending on the curve type:
  1424. BOOL bSuccess;
  1425. switch(arctype)
  1426. {
  1427. case ARCTYPE_ARCTO:
  1428. case ARCTYPE_ARC:
  1429. bSuccess = pso.bStroke(dco, dco.plaRealize(exo), &exo);
  1430. break;
  1431. case ARCTYPE_CHORD:
  1432. case ARCTYPE_PIE:
  1433. bSuccess = pso.bStrokeAndFill(dco, dco.plaRealize(exo), &exo);
  1434. break;
  1435. }
  1436. return(bSuccess);
  1437. }
  1438. /******************************Public*Routine******************************\
  1439. * BOOL bEllipse(epo, ebox)
  1440. *
  1441. * Adds an ellipse to the path. Used by 'Ellipse' and 'CreateEllipticalRgn'
  1442. * APIs.
  1443. *
  1444. * 4 Beziers are used, one for each quadrant of the ellipse. Drawing
  1445. * starts at the positive x-axis, and proceeds in a counter-clockwise
  1446. * direction.
  1447. *
  1448. * History:
  1449. * 27-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  1450. * Wrote it.
  1451. \**************************************************************************/
  1452. BOOL bEllipse
  1453. (
  1454. EPATHOBJ& epo,
  1455. EBOX& ebox // Bounding box
  1456. )
  1457. {
  1458. ASSERTGDI(epo.bValid(), "bEllipse: Bad object parm\n");
  1459. // 'eptlC' and 'eptlD' are vectors from the corners of the bounding
  1460. // box that are where the inner control points of the Beziers are
  1461. // placed:
  1462. EPOINTL eptlC;
  1463. EPOINTL eptlD;
  1464. LONGLONG eqTmp;
  1465. vEllipseControlsIn((VECTORFX*) &ebox.eptlA, (VECTORFX*) &eptlC, &eqTmp);
  1466. vEllipseControlsIn((VECTORFX*) &ebox.eptlB, (VECTORFX*) &eptlD, &eqTmp);
  1467. BOOL bSuccess;
  1468. {
  1469. // Start drawing at the 'x-axis':
  1470. EPOINTL eptlStart = ebox.aeptl[3];
  1471. eptlStart += ebox.eptlB;
  1472. bSuccess = epo.bMoveTo((PEXFORMOBJ) NULL, &eptlStart);
  1473. }
  1474. // Use one 96 byte buffer so that we only have to call bPolyBezierTo
  1475. // once. I would declare this as an array of EPOINTLs, but the
  1476. // compiler doesn't like the static constructors:
  1477. EAPOINTL aeptl[12];
  1478. // First Quadrant
  1479. aeptl[0] = ebox.aeptl[0]; aeptl[0] -= eptlD;
  1480. aeptl[1] = ebox.aeptl[0]; aeptl[1] -= eptlC;
  1481. aeptl[2] = ebox.aeptl[0]; aeptl[2] -= ebox.eptlA;
  1482. // Second Quadrant:
  1483. aeptl[3] = ebox.aeptl[1]; aeptl[3] += eptlC;
  1484. aeptl[4] = ebox.aeptl[1]; aeptl[4] -= eptlD;
  1485. aeptl[5] = ebox.aeptl[1]; aeptl[5] -= ebox.eptlB;
  1486. // Third Quadrant:
  1487. aeptl[6] = ebox.aeptl[2]; aeptl[6] += eptlD;
  1488. aeptl[7] = ebox.aeptl[2]; aeptl[7] += eptlC;
  1489. aeptl[8] = ebox.aeptl[2]; aeptl[8] += ebox.eptlA;
  1490. // Fourth Quadrant:
  1491. aeptl[9] = ebox.aeptl[3]; aeptl[9] -= eptlC;
  1492. aeptl[10] = ebox.aeptl[3]; aeptl[10] += eptlD;
  1493. aeptl[11] = ebox.aeptl[3]; aeptl[11] += ebox.eptlB;
  1494. return(epo.bPolyBezierTo((PEXFORMOBJ) NULL, aeptl, 12) &&
  1495. epo.bCloseFigure());
  1496. }
  1497. /******************************Public*Routine******************************\
  1498. * BOOL bRoundRect(epo, exoWorldToDevice, ebox, x, y)
  1499. *
  1500. * Adds a rounded rectangle to the path. Used by 'RoundRect' and
  1501. * 'CreateRoundRectRgn' APIs.
  1502. *
  1503. * 4 Beziers and 4 lines are used. Drawing starts at the first curve
  1504. * in the "upper right" corner of the rounded rectangle, and proceeds
  1505. * in a "counter-clockwise" direction.
  1506. *
  1507. * This routine constructs the RoundRect by taking advantage of the fact
  1508. * that all the control points defining the lines and the Beziers lie on
  1509. * the bounding box.
  1510. *
  1511. * History:
  1512. * 27-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  1513. * Wrote it.
  1514. \**************************************************************************/
  1515. BOOL bRoundRect
  1516. (
  1517. EPATHOBJ& epo,
  1518. EBOX& ebox, // Bound box of rectangle
  1519. LONG x, // Width of ellipse in World coordinates
  1520. LONG y // Height of ellipse in World coordinates
  1521. )
  1522. {
  1523. ASSERTGDI(epo.bValid(), "bRoundRect: Bad object parm\n");
  1524. EFLOAT efdx = efHalfDiff(ebox.rclWorld.left, ebox.rclWorld.right);
  1525. EFLOAT efdy = efHalfDiff(ebox.rclWorld.top, ebox.rclWorld.bottom);
  1526. EFLOATEXT efFractionA;
  1527. EFLOATEXT efFractionB;
  1528. if (efdx.bIsZero() || efdy.bIsZero())
  1529. {
  1530. efFractionA = FP_0_0;
  1531. efFractionB = FP_0_0;
  1532. }
  1533. else
  1534. {
  1535. // Old Windows takes the absolute values of the ellipse dimensions
  1536. // used for drawing the corners:
  1537. x = ABS(x);
  1538. y = ABS(y);
  1539. // Determine what fraction of the bound box that the ellipse
  1540. // comprises:
  1541. efdx.vAbs();
  1542. efdy.vAbs();
  1543. efFractionA = x;
  1544. efFractionB = y;
  1545. efFractionA /= efdx;
  1546. efFractionB /= efdy;
  1547. }
  1548. //
  1549. // If the ellipse given by the user has larger dimensions than the
  1550. // bound box, shrink those dimensions so that they are the same as
  1551. // the bound box:
  1552. //
  1553. if (efFractionA > FP_2_0)
  1554. efFractionA = FP_1_0;
  1555. else
  1556. efFractionA.vDivBy2();
  1557. if (efFractionB > FP_2_0)
  1558. efFractionB = FP_1_0;
  1559. else
  1560. efFractionB.vDivBy2();
  1561. //DbgPrint("FracA: %li FracB: %li\n", lConv(efdx), lConv(efdy));
  1562. //
  1563. // 'eptlX' and 'eptlY' are the vectors from the vertices of the
  1564. // bounding box defining the start and end-points of the Beziers
  1565. // used for the rounded corners. 'eptlXprime' and 'eptlYprime'
  1566. // define the inner two control points (all the Beziers' control
  1567. // points lie on the bounding box):
  1568. //
  1569. // -------------------> ebox.eptlA
  1570. //
  1571. // : :
  1572. // | |
  1573. // ^ |\ /|
  1574. // | | \ / |
  1575. // | ^ | \ / |
  1576. // eptlY | eptlYprime | 2--------------------------------------3
  1577. //
  1578. // --> eptlXprime
  1579. //
  1580. // ------> eptlX
  1581. //
  1582. // eptlX = efFractionA * ebox.eptlA
  1583. // eptlY = efFractionB * ebox.eptlB
  1584. //
  1585. EPOINTL eptlX;
  1586. EPOINTL eptlY;
  1587. EPOINTL eptlXprime;
  1588. EPOINTL eptlYprime;
  1589. {
  1590. LONGLONG eqTmp;
  1591. EPOINTFL eptefX;
  1592. EPOINTFL eptefY;
  1593. eptefX = ebox.eptlA;
  1594. eptefY = ebox.eptlB;
  1595. eptefX *= efFractionA;
  1596. eptefY *= efFractionB;
  1597. // This conversion should not fail:
  1598. eptefX.bToPOINTL(eptlX);
  1599. eptefY.bToPOINTL(eptlY);
  1600. // We now know where to put the end-points of the bezier curves. Now
  1601. // compute the inner control points:
  1602. vEllipseControlsIn((VECTORFX*) &eptlX, (VECTORFX*) &eptlXprime, &eqTmp);
  1603. vEllipseControlsIn((VECTORFX*) &eptlY, (VECTORFX*) &eptlYprime, &eqTmp);
  1604. }
  1605. EAPOINTL aeptl[3];
  1606. EPOINTL eptl;
  1607. BOOL bFailure = FALSE; // Fail by default
  1608. eptl = ebox.aeptl[0];
  1609. eptl -= eptlY;
  1610. if (!epo.bMoveTo((PEXFORMOBJ) NULL, &eptl))
  1611. return(bFailure);
  1612. aeptl[0] = ebox.aeptl[0]; aeptl[0] -= eptlYprime;
  1613. aeptl[1] = ebox.aeptl[0]; aeptl[1] -= eptlXprime;
  1614. aeptl[2] = ebox.aeptl[0]; aeptl[2] -= eptlX;
  1615. if (!epo.bPolyBezierTo((PEXFORMOBJ) NULL, aeptl, 3))
  1616. return(bFailure);
  1617. // Quadrant two:
  1618. eptl = ebox.aeptl[1];
  1619. eptl += eptlX;
  1620. if (!epo.bPolyLineTo((PEXFORMOBJ) NULL, &eptl, 1))
  1621. return(bFailure);
  1622. aeptl[0] = ebox.aeptl[1]; aeptl[0] += eptlXprime;
  1623. aeptl[1] = ebox.aeptl[1]; aeptl[1] -= eptlYprime;
  1624. aeptl[2] = ebox.aeptl[1]; aeptl[2] -= eptlY;
  1625. if (!epo.bPolyBezierTo((PEXFORMOBJ) NULL, aeptl, 3))
  1626. return(bFailure);
  1627. // Quadrant three:
  1628. eptl = ebox.aeptl[2];
  1629. eptl += eptlY;
  1630. if (!epo.bPolyLineTo((PEXFORMOBJ) NULL, &eptl, 1))
  1631. return(bFailure);
  1632. aeptl[0] = ebox.aeptl[2]; aeptl[0] += eptlYprime;
  1633. aeptl[1] = ebox.aeptl[2]; aeptl[1] += eptlXprime;
  1634. aeptl[2] = ebox.aeptl[2]; aeptl[2] += eptlX;
  1635. if (!epo.bPolyBezierTo((PEXFORMOBJ) NULL, aeptl, 3))
  1636. return(bFailure);
  1637. // Quadrant four:
  1638. eptl = ebox.aeptl[3];
  1639. eptl -= eptlX;
  1640. if (!epo.bPolyLineTo((PEXFORMOBJ) NULL, &eptl, 1))
  1641. return(bFailure);
  1642. aeptl[0] = ebox.aeptl[3]; aeptl[0] -= eptlXprime;
  1643. aeptl[1] = ebox.aeptl[3]; aeptl[1] += eptlYprime;
  1644. aeptl[2] = ebox.aeptl[3]; aeptl[2] += eptlY;
  1645. if (!epo.bPolyBezierTo((PEXFORMOBJ) NULL, aeptl, 3))
  1646. return(bFailure);
  1647. // Done:
  1648. return(epo.bCloseFigure());
  1649. }
  1650. /******************************Public*Routine******************************\
  1651. * BOOL bPolyPolygon(epo, exo, pptl, pcptl, ccptl, cMaxPoints)
  1652. *
  1653. * Adds a PolyPolygon to the path. Used by CreatePolyPolygonRgn and
  1654. * PolyPolygon APIs. Returns FALSE if fails, and sets last error code.
  1655. *
  1656. * History:
  1657. * 27-Nov-1990 -by- J. Andrew Goossen [andrewgo]
  1658. * Wrote it.
  1659. \**************************************************************************/
  1660. BOOL bPolyPolygon
  1661. (
  1662. EPATHOBJ& epo,
  1663. EXFORMOBJ& exo,
  1664. PPOINTL pptl,
  1665. LONG* pcptl,
  1666. ULONG ccptl,
  1667. LONG cMaxPoints
  1668. )
  1669. {
  1670. ASSERTGDI(epo.bValid(), "bPolyPolygon: Bad epo\n");
  1671. ASSERTGDI(exo.bValid(), "bPolyPolygon: Bad exo\n");
  1672. if (ccptl == 0)
  1673. return(TRUE);
  1674. LONG cPts;
  1675. LONG* pcptlEnd = pcptl + ccptl;
  1676. // Now add to the path:
  1677. do {
  1678. // We have to be careful to make a local copy of this polygon's point
  1679. // count (by copying to to cPts) to get the value out of the shared
  1680. // client/server memory window, where the app could trash the value at
  1681. // any time:
  1682. cPts = *pcptl;
  1683. cMaxPoints -= cPts;
  1684. // Check parameters. Each polygon must have at least 2 points.
  1685. if (cMaxPoints < 0 || cPts < 2)
  1686. {
  1687. SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
  1688. return(FALSE);
  1689. }
  1690. if (!epo.bMoveTo(&exo, pptl) ||
  1691. !epo.bPolyLineTo(&exo, pptl + 1, cPts - 1) ||
  1692. !epo.bCloseFigure())
  1693. return(FALSE);
  1694. pptl += cPts;
  1695. pcptl++;
  1696. } while (pcptl < pcptlEnd);
  1697. return(TRUE);
  1698. }