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.

5541 lines
146 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1999 - 2000 Microsoft Corporation
  4. *
  5. * Module Name:
  6. *
  7. * PathWidener.cpp
  8. *
  9. * Abstract:
  10. *
  11. * Implementation of the GpPathWidener class
  12. *
  13. * Revision History:
  14. *
  15. * 11/23/99 ikkof
  16. * Created it
  17. *
  18. \**************************************************************************/
  19. #include "precomp.hpp"
  20. // 4*(REALSQRT(2.0) - 1)/3
  21. #define U_CIR ((REAL)(0.552284749))
  22. // Define DEBUG_PATHWIDENER if debugging is necessary.
  23. //#define DEBUG_PATHWIDENER
  24. GpStatus ReversePath(INT count,GpPointF* points,BYTE* types);
  25. const BOOL USE_POLYGON_JOIN = FALSE;
  26. INT
  27. CombinePaths(
  28. INT count,
  29. GpPointF* points,
  30. BYTE* types,
  31. INT count1,
  32. const GpPointF* points1,
  33. const BYTE* types1,
  34. BOOL forward1,
  35. INT count2,
  36. const GpPointF* points2,
  37. const BYTE* types2,
  38. BOOL forward2,
  39. BOOL connect
  40. );
  41. GpStatus
  42. CalculateGradientArray(
  43. GpPointF* grad,
  44. REAL* distances,
  45. const GpPointF* points,
  46. INT count
  47. );
  48. GpStatus
  49. GetMajorAndMinorAxis(
  50. REAL* majorR,
  51. REAL* minorR,
  52. const GpMatrix* matrix
  53. );
  54. enum
  55. {
  56. WideningClosed = 1,
  57. WideningFirstType = 2,
  58. WideningLastType = 4,
  59. WideningLastPointSame = 8,
  60. WideningNeedsToAdjustNormals = 16,
  61. WideningUseBevelJoinInside = 32
  62. };
  63. enum GpTurningDirection
  64. {
  65. NotTurning = 0,
  66. TurningBack = 1,
  67. TurningRight = 2,
  68. TurningLeft = 3,
  69. NotMoving = -1
  70. };
  71. GpTurningDirection
  72. getJoin(
  73. GpLineJoin lineJoin,
  74. const GpPointF& point,
  75. const GpPointF& grad1,
  76. const GpPointF& grad2,
  77. const GpPointF& norm1,
  78. const GpPointF& norm2,
  79. REAL leftWidth,
  80. REAL rightWidth,
  81. INT *leftCount,
  82. GpPointF *leftPoints,
  83. BOOL *leftInside,
  84. INT *rightCount,
  85. GpPointF *rightPoints,
  86. BOOL *rightInside,
  87. BOOL needsToAdjustNormals,
  88. REAL miterLimit2
  89. );
  90. /**************************************************************************\
  91. *
  92. * Function Description:
  93. *
  94. * This reverses the path data.
  95. *
  96. * Arguments:
  97. *
  98. * [IN] count - the number of points.
  99. * [IN/OUT] points - the data points to be reversed.
  100. * [IN/OUT] types - the data types to be reversed.
  101. *
  102. * Return Value:
  103. *
  104. * Status
  105. *
  106. \**************************************************************************/
  107. GpStatus
  108. ReversePath(
  109. INT count,
  110. GpPointF* points,
  111. BYTE* types
  112. )
  113. {
  114. DpPathTypeIterator iter(types, count);
  115. if(!iter.IsValid())
  116. return InvalidParameter;
  117. INT startIndex, endIndex;
  118. BOOL isClosed;
  119. BOOL isStartDashMode, isEndDashMode;
  120. BOOL wasMarkerEnd = FALSE;
  121. INT i;
  122. while(iter.NextSubpath(&startIndex, &endIndex, &isClosed))
  123. {
  124. if((types[startIndex] & PathPointTypeDashMode) != 0)
  125. isStartDashMode = TRUE;
  126. else
  127. isStartDashMode = FALSE;
  128. if((types[endIndex] & PathPointTypeDashMode) != 0)
  129. isEndDashMode = TRUE;
  130. else
  131. isEndDashMode = FALSE;
  132. BOOL isMarkerEnd
  133. = (types[endIndex] & PathPointTypePathMarker) != 0;
  134. BYTE startType = types[startIndex]; // Save the first type.
  135. // Shift type points.
  136. for(i = startIndex + 1; i <= endIndex; i++)
  137. {
  138. types[i - 1] = types[i];
  139. }
  140. // Clear the close subpapth flag for original type (now at endIndex - 1).
  141. if(endIndex > 0)
  142. types[endIndex - 1] &= ~PathPointTypeCloseSubpath;
  143. types[endIndex] = PathPointTypeStart;
  144. if(isStartDashMode)
  145. types[startIndex] |= PathPointTypeDashMode;
  146. else
  147. types[startIndex] &= ~PathPointTypeDashMode;
  148. if(isEndDashMode)
  149. types[endIndex] |= PathPointTypeDashMode;
  150. else
  151. types[endIndex] &= ~PathPointTypeDashMode;
  152. // Add the dash and close flag.
  153. if(isClosed)
  154. types[startIndex] |= PathPointTypeCloseSubpath;
  155. else
  156. types[startIndex] &= ~PathPointTypeCloseSubpath;
  157. // Shift the marker flag by 1 from the original position.
  158. // This means we have to shift by 2 since the types array
  159. // was shifted by -1.
  160. for(i = endIndex; i >= startIndex + 2; i--)
  161. {
  162. if(types[i - 2] & PathPointTypePathMarker)
  163. types[i] |= PathPointTypePathMarker;
  164. else
  165. types[i] &= ~PathPointTypePathMarker;
  166. }
  167. // Shift Marker flag from the startIndex.
  168. if(startType & PathPointTypePathMarker)
  169. types[startIndex + 1] |= PathPointTypePathMarker;
  170. else
  171. types[startIndex + 1] &= ~PathPointTypePathMarker;
  172. // Shift Marker flag from the end of the previous subpath.
  173. if(wasMarkerEnd)
  174. types[startIndex] |= PathPointTypePathMarker;
  175. else
  176. types[startIndex] &= ~PathPointTypePathMarker;
  177. wasMarkerEnd = isMarkerEnd;
  178. // Keep the location of the internal flag. So we must
  179. // shift back by 1.
  180. for(i = endIndex; i >= startIndex + 1; i--)
  181. {
  182. if(types[i - 1] & PathPointTypeInternalUse)
  183. types[i] |= PathPointTypeInternalUse;
  184. else
  185. types[i] &= ~PathPointTypeInternalUse;
  186. }
  187. if(startType & PathPointTypeInternalUse)
  188. types[startIndex] |= PathPointTypeInternalUse;
  189. else
  190. types[startIndex] &= ~PathPointTypeInternalUse;
  191. }
  192. // Reverse the points and types data.
  193. INT halfCount = count/2;
  194. for(i = 0; i < halfCount; i++)
  195. {
  196. GpPointF tempPt;
  197. BYTE tempType;
  198. tempPt = points[count - 1 - i];
  199. tempType = types[count - 1 - i];
  200. points[count - 1 - i] = points[i];
  201. types[count -1 - i] = types[i];
  202. points[i] = tempPt;
  203. types[i] = tempType;
  204. }
  205. #ifdef DEBUG_PATHWIDENER
  206. DpPathTypeIterator iter2(types, count);
  207. if(!iter2.IsValid())
  208. {
  209. WARNING(("ReversePath: failed."));
  210. return GenericError;
  211. }
  212. #endif
  213. return Ok;
  214. }
  215. /**************************************************************************\
  216. *
  217. * Function Description:
  218. *
  219. * This combines the two open segments each of which is continuous.
  220. * The data is returned to points1 and types1. They must be allocated
  221. * at least the array size of count1 + count2.
  222. *
  223. * Arguments:
  224. *
  225. * [IN] count1 - the number of points of the first path.
  226. * [IN/OUT] points1 - the first path points.
  227. * [IN/OUT] types1 - the first path types.
  228. * [IN] forward1 - the direction of the first path. TRUE if forward.
  229. * [IN] count2 - the number of points of the second path.
  230. * [IN] points2 - the second path points.
  231. * [IN] types2 - the second path types.
  232. * [IN] forward2 - the direction of the second path. TRUE if forward.
  233. *
  234. * Return Value:
  235. *
  236. * The total number of points of the combined path.
  237. *
  238. \**************************************************************************/
  239. INT
  240. combineTwoOpenSegments(
  241. INT count1,
  242. GpPointF* points1,
  243. BYTE* types1,
  244. BOOL forward1,
  245. INT count2,
  246. GpPointF* points2,
  247. BYTE* types2,
  248. BOOL forward2
  249. )
  250. {
  251. GpStatus status = Ok;
  252. if(
  253. count1 < 0 || !points1 || !types1 ||
  254. count2 < 0 || !points2 || !types2
  255. )
  256. return 0;
  257. if(!forward1 && count1 > 0)
  258. {
  259. status = ::ReversePath(count1, points1, types1);
  260. if(status != Ok)
  261. return 0;
  262. }
  263. if(!forward2 && count2 > 0)
  264. {
  265. status = ::ReversePath(count2, points2, types2);
  266. if(status != Ok)
  267. return 0;
  268. }
  269. INT offset = 0;
  270. if(count1 > 0 && count2 > 0)
  271. {
  272. if(REALABS(points1[count1 - 1].X - points2[0].X) +
  273. REALABS(points1[count1 - 1].Y - points2[0].Y)
  274. < POINTF_EPSILON
  275. )
  276. offset = 1;
  277. }
  278. if(count2 - offset > 0)
  279. {
  280. GpMemcpy(
  281. points1 + count1,
  282. points2 + offset,
  283. (count2 - offset)*sizeof(GpPointF)
  284. );
  285. GpMemcpy(
  286. types1 + count1,
  287. types2 + offset,
  288. count2 - offset
  289. );
  290. }
  291. BYTE saveType = types1[0];
  292. types1[0] = PathPointTypeLine |
  293. (saveType & ~PathPointTypePathTypeMask);
  294. // Make sure the first path is not closed.
  295. if(count1 > 0)
  296. {
  297. if(types1[count1 - 1] & PathPointTypeCloseSubpath)
  298. types1[count1 - 1] &= ~PathPointTypeCloseSubpath;
  299. }
  300. // Set the first point type of the second path correctly.
  301. if(offset == 0)
  302. {
  303. saveType = types1[count1];
  304. types1[count1] = PathPointTypeLine |
  305. (saveType & ~PathPointTypePathTypeMask);
  306. }
  307. // Make sure this path is not closed.
  308. INT total = count1 + count2 - offset;
  309. if(total > 0)
  310. {
  311. if(types1[total - 1] & PathPointTypeCloseSubpath)
  312. types1[total - 1] &= ~PathPointTypeCloseSubpath;
  313. }
  314. return total;
  315. }
  316. /**************************************************************************\
  317. *
  318. * Function Description:
  319. *
  320. * This combines the two closed segments each of which is made of closed
  321. * segments.
  322. * The data is returned to points1 and types1. They must be allocated
  323. * at least the array size of count1 + count2.
  324. *
  325. * Arguments:
  326. *
  327. * [IN] count1 - the number of points of the first path.
  328. * [IN/OUT] points1 - the first path points.
  329. * [IN/OUT] types1 - the first path types.
  330. * [IN] forward1 - the direction of the first path. TRUE if forward.
  331. * [IN] count2 - the number of points of the second path.
  332. * [IN] points2 - the second path points.
  333. * [IN] types2 - the second path types.
  334. * [IN] forward2 - the direction of the second path. TRUE if forward.
  335. *
  336. * Return Value:
  337. *
  338. * The total number of points of the combined path.
  339. *
  340. \**************************************************************************/
  341. INT
  342. combineClosedSegments(
  343. INT count1,
  344. GpPointF* points1,
  345. BYTE* types1,
  346. BOOL forward1,
  347. INT count2,
  348. GpPointF* points2,
  349. BYTE* types2,
  350. BOOL forward2
  351. )
  352. {
  353. GpStatus status = Ok;
  354. if(
  355. count1 < 0 || !points1 || !types1 ||
  356. count2 < 0 || !points2 || !types2
  357. )
  358. return 0;
  359. if(count1 == 0 && count2 == 0)
  360. return 0;
  361. if(!forward1 && count1 > 0)
  362. {
  363. status = ::ReversePath(count1, points1, types1);
  364. if(status != Ok)
  365. return 0;
  366. }
  367. if(!forward2 && count2 > 0)
  368. {
  369. status = ::ReversePath(count2, points2, types2);
  370. if(status != Ok)
  371. return 0;
  372. }
  373. // Make sure the first path is closed.
  374. types1[0] = PathPointTypeStart;
  375. if(count1 > 0)
  376. {
  377. if((types1[count1 - 1] & PathPointTypeCloseSubpath) == 0)
  378. types1[count1 - 1] |= PathPointTypeCloseSubpath;
  379. }
  380. INT total = count1 + count2;
  381. if(count2 > 0)
  382. {
  383. GpMemcpy(points1 + count1, points2, count2*sizeof(GpPointF));
  384. GpMemcpy(types1 + count1, types2, count2);
  385. BYTE saveType = types1[count1];
  386. types1[count1] = PathPointTypeStart |
  387. (saveType & ~PathPointTypePathTypeMask);
  388. // Make sure the second path is closed.
  389. types1[total - 1] |= PathPointTypeCloseSubpath;
  390. }
  391. return total;
  392. }
  393. /**************************************************************************\
  394. *
  395. * Function Description:
  396. *
  397. * This combines the two data points. This is a general algorithm.
  398. * The output buffers (points and types) can be the same as the
  399. * first input buffers (points1 and types1). In that case, both
  400. * buffers must be allocated at least to the array size of
  401. * count1 + count2.
  402. *
  403. * Arguments:
  404. *
  405. * [IN] count - the allocated number of points (>= count1 + count2).
  406. * [OUT] points - the combined data points.
  407. * [OUT] types - the combined data types.
  408. * [IN] count1 - the number of points of the first path.
  409. * [IN] points1 - the first path points.
  410. * [IN] types1 - the first path types.
  411. * [IN] forward1 - the direction of the first path. TRUE if forward.
  412. * [IN] count2 - the number of points of the second path.
  413. * [IN] points2 - the second path points.
  414. * [IN] types2 - the second path types.
  415. * [IN] forward2 - the direction of the second path. TRUE if forward.
  416. * [IN] connect - TRUE if the second line needs to be connected.
  417. *
  418. * Return Value:
  419. *
  420. * The total number of points of the combined path.
  421. *
  422. \**************************************************************************/
  423. INT
  424. CombinePaths(
  425. INT count,
  426. GpPointF* points,
  427. BYTE* types,
  428. INT count1,
  429. const GpPointF* points1,
  430. const BYTE* types1,
  431. BOOL forward1,
  432. INT count2,
  433. const GpPointF* points2,
  434. const BYTE* types2,
  435. BOOL forward2,
  436. BOOL connect
  437. )
  438. {
  439. if(!points || !types || count < count1 + count2
  440. || count1 < 0 || !points1 || !types1
  441. || count2 < 0 || !points2 || !types2)
  442. return 0;
  443. // Check if the returning buffers are the same as the
  444. // first input buffers.
  445. INT resultCount = 0;
  446. if(points != points1 || types != types1)
  447. {
  448. if(points == points1 || types == types1)
  449. {
  450. // The both output buffer must be different.
  451. // If either of them is the same, don't combine
  452. // the path.
  453. return 0;
  454. }
  455. if(count1 > 0)
  456. {
  457. // Copy the first path.
  458. DpPathIterator iter1(points1, types1, count1);
  459. if(!iter1.IsValid())
  460. return 0;
  461. resultCount = iter1.Enumerate(points, types, count1);
  462. if(resultCount <= 0)
  463. return 0;
  464. }
  465. }
  466. else
  467. {
  468. // Both output buffers are the same as the first output
  469. // buffers.
  470. resultCount = count1;
  471. }
  472. GpStatus status = Ok;
  473. BOOL path1Closed;
  474. if(!forward1 && resultCount > 0)
  475. {
  476. status = ::ReversePath(resultCount, points, types);
  477. if(status != Ok)
  478. return 0;
  479. }
  480. if(count2 <= 0)
  481. {
  482. // No need to add the second path.
  483. return resultCount;
  484. }
  485. // Regard the empty path as a closed path.
  486. path1Closed = TRUE;
  487. if(resultCount > 0)
  488. {
  489. // Check the last point of path1.
  490. if((types[resultCount - 1] & PathPointTypeCloseSubpath))
  491. path1Closed = TRUE;
  492. else
  493. path1Closed = FALSE;
  494. }
  495. INT totalCount = 0;
  496. totalCount += resultCount;
  497. DpPathIterator iter2(points2, types2, count2);
  498. if(!iter2.IsValid())
  499. return 0;
  500. GpPointF* pts2 = points + resultCount;
  501. BYTE* typs2 = types + resultCount;
  502. resultCount = iter2.Enumerate(pts2, typs2, count2);
  503. if(resultCount <= 0)
  504. return 0;
  505. if(!forward2)
  506. {
  507. status = ::ReversePath(resultCount, pts2, typs2);
  508. if(status != Ok)
  509. return 0;
  510. }
  511. // Check if the first subpath of path2 is closed or not.
  512. BOOL path2Closed;
  513. DpPathTypeIterator iter3(typs2, resultCount);
  514. if(!iter3.IsValid())
  515. return 0;
  516. INT startIndex, endIndex;
  517. iter3.NextSubpath(&startIndex, &endIndex, &path2Closed);
  518. BYTE saveType= typs2[0];
  519. if(path1Closed || path2Closed)
  520. {
  521. typs2[0] = PathPointTypeStart |
  522. (saveType & ~PathPointTypePathTypeMask);
  523. }
  524. else
  525. {
  526. // Both paths are opened.
  527. if(connect)
  528. {
  529. typs2[0] = PathPointTypeLine |
  530. (saveType & ~PathPointTypePathTypeMask);
  531. // Check if the end point of path1 and the start point of path2
  532. // are the same. If so, skip this point.
  533. if(REALABS(pts2[-1].X - pts2[0].X)
  534. + REALABS(pts2[-1].Y - pts2[0].Y) < POINTF_EPSILON)
  535. {
  536. for(INT i = 0; i < resultCount - 1; i++)
  537. {
  538. pts2[i] = pts2[i + 1];
  539. typs2[i] = typs2[i + 1];
  540. }
  541. resultCount--;
  542. }
  543. }
  544. else
  545. {
  546. typs2[0] = PathPointTypeStart |
  547. (saveType & ~PathPointTypePathTypeMask);
  548. }
  549. }
  550. totalCount += resultCount;
  551. return totalCount;
  552. }
  553. /**************************************************************************\
  554. *
  555. * Function Description:
  556. *
  557. * Removes the degenerate points and copy only non-degenerate points.
  558. * It is assumed that points and types array are allocated so that
  559. * they can hold at least "count" number of elements.
  560. *
  561. * Arguments:
  562. *
  563. * [IN] pathType - the type of the path data to be added.
  564. * [OUT] points - the copied data points.
  565. * [OUT] types - the copied data types.
  566. * [IN] dataPoints - the original data points.
  567. * [IN] count - the number of the original data points.
  568. * [IN/OUT] lastPt - the last point.
  569. *
  570. * Return Value:
  571. *
  572. * The total number of copied points.
  573. *
  574. \**************************************************************************/
  575. INT copyNonDegeneratePoints(
  576. BYTE pathType,
  577. GpPointF* points,
  578. BYTE* types,
  579. const GpPointF* dataPoints,
  580. const BYTE* dataTypes,
  581. INT count,
  582. GpPointF* lastPt
  583. )
  584. {
  585. GpPointF nextPt;
  586. INT addedCount = 0;
  587. if(pathType == PathPointTypeLine)
  588. {
  589. // Add only the different points.
  590. for(INT i = 0; i < count; i++)
  591. {
  592. nextPt = *dataPoints++;
  593. if( (REALABS(nextPt.X - lastPt->X) > REAL_EPSILON) ||
  594. (REALABS(nextPt.Y - lastPt->Y) > REAL_EPSILON) )
  595. {
  596. *points++ = nextPt;
  597. *lastPt = nextPt;
  598. addedCount++;
  599. }
  600. }
  601. if(addedCount > 0)
  602. {
  603. GpMemset(types, pathType, addedCount);
  604. }
  605. }
  606. else
  607. {
  608. // In case of Bezier, we need to do
  609. // degenerate case test for future.
  610. addedCount = count;
  611. if(addedCount > 0)
  612. {
  613. if(dataTypes)
  614. {
  615. GpMemcpy(types, dataTypes, addedCount);
  616. }
  617. else
  618. {
  619. GpMemset(types, pathType, addedCount);
  620. }
  621. GpMemcpy(
  622. points,
  623. dataPoints,
  624. addedCount*sizeof(GpPointF)
  625. );
  626. }
  627. else
  628. {
  629. addedCount = 0;
  630. }
  631. }
  632. return addedCount;
  633. }
  634. /**************************************************************************\
  635. *
  636. * Function Description:
  637. *
  638. * This calculates the major and minor radius of an oval
  639. * when the unit cricle is transformed by the given matrix.
  640. * For further details, see ikkof's notes on Pen Transform.
  641. *
  642. * Arguments:
  643. *
  644. * [OUT] majorR - the major radius.
  645. * [OUT] minorR - the minor radius.
  646. * [IN] matrix - the matrix to transform the unit circle.
  647. *
  648. * Return Value:
  649. *
  650. * Status
  651. *
  652. * 01/28/00 ikkof
  653. * Created it
  654. *
  655. \**************************************************************************/
  656. GpStatus
  657. GetMajorAndMinorAxis(REAL* majorR, REAL* minorR, const GpMatrix* matrix)
  658. {
  659. if(matrix == NULL)
  660. {
  661. // Regard this as an identity matrix.
  662. *majorR = 1;
  663. *minorR = 1;
  664. return Ok;
  665. }
  666. REAL m11 = matrix->GetM11();
  667. REAL m12 = matrix->GetM12();
  668. REAL m21 = matrix->GetM21();
  669. REAL m22 = matrix->GetM22();
  670. REAL d1 = ((m11*m11 + m12*m12) - (m21*m21 + m22*m22))/2;
  671. REAL d2 = m11*m21 + m12*m22;
  672. REAL D = d1*d1 + d2*d2;
  673. if(D > 0)
  674. D = REALSQRT(D);
  675. REAL r0 = (m11*m11 + m12*m12 + m21*m21 + m22*m22)/2;
  676. REAL r1 = REALSQRT(r0 + D);
  677. REAL r2 = REALSQRT(r0 - D);
  678. // They should be positive numbers. Prevent the floating
  679. // point underflow.
  680. if(r1 <= CPLX_EPSILON)
  681. r1 = CPLX_EPSILON;
  682. if(r2 <= CPLX_EPSILON)
  683. r2 = CPLX_EPSILON;
  684. *majorR = r1;
  685. *minorR = r2;
  686. return Ok;
  687. }
  688. VOID
  689. GpPathWidener::Initialize(
  690. const GpPointF* points,
  691. const BYTE* types,
  692. INT count,
  693. const DpPen* pen,
  694. const GpMatrix* matrix,
  695. REAL dpiX, // These parameters are not really used
  696. REAL dpiY, //
  697. BOOL isAntiAliased, // This one is definitely not used.
  698. BOOL isInsetPen
  699. )
  700. {
  701. SetValid(FALSE);
  702. Inset1 = 0;
  703. Inset2 = 0;
  704. NeedsToTransform = FALSE;
  705. IsAntiAliased = isAntiAliased;
  706. // nothing to widen, so return an invalid widener.
  707. if( (!pen) || (count == 0) )
  708. {
  709. return;
  710. }
  711. Pen = pen;
  712. GpUnit unit = Pen->Unit;
  713. InsetPenMode = isInsetPen;
  714. if(unit == UnitWorld)
  715. NeedsToTransform = FALSE;
  716. else
  717. NeedsToTransform = TRUE;
  718. GpMatrix penTrans = ((DpPen*) Pen)->Xform;
  719. BOOL hasPenTransform = FALSE;
  720. if(!NeedsToTransform && !penTrans.IsTranslate())
  721. {
  722. hasPenTransform = TRUE;
  723. penTrans.RemoveTranslation();
  724. }
  725. if(matrix)
  726. XForm = *matrix; // Otherwise XForm remains Identity.
  727. if(hasPenTransform)
  728. {
  729. XForm.Prepend(penTrans);
  730. }
  731. DpiX = dpiX;
  732. DpiY = dpiY;
  733. // 0 means use the Desktop DPI
  734. if ((REALABS(DpiX) < REAL_EPSILON) ||
  735. (REALABS(DpiY) < REAL_EPSILON) )
  736. {
  737. DpiX = Globals::DesktopDpiX;
  738. DpiY = Globals::DesktopDpiY;
  739. }
  740. StrokeWidth = Pen->Width;
  741. if(!NeedsToTransform)
  742. {
  743. REAL majorR, minorR;
  744. ::GetMajorAndMinorAxis(&majorR, &minorR, &XForm);
  745. MaximumWidth = StrokeWidth*majorR;
  746. MinimumWidth = StrokeWidth*minorR;
  747. UnitScale = min (majorR, minorR);
  748. }
  749. else
  750. {
  751. UnitScale = ::GetDeviceWidth(1.0f, unit, dpiX);
  752. StrokeWidth = UnitScale * StrokeWidth;
  753. MinimumWidth = StrokeWidth;
  754. }
  755. OriginalStrokeWidth = StrokeWidth;
  756. // Set minimum width to 1.0 (plus a bit for possible precision errors),
  757. // so that narrow width pens don't end up leaving gaps in the line.
  758. REAL minWidth = 1.000001f;
  759. if(InsetPenMode)
  760. {
  761. minWidth *= 2.0f;
  762. // Dashes smaller than a pixel are dropping out entirely in inset
  763. // pen because of the rasterizer pixel level clipping that is taking
  764. // place. We increase the minimum width of dashed lines making them
  765. // roughly 4.0f. This also helps address the weird moire aliasing
  766. // effects with the really small dash-dot round lines.
  767. if(Pen->DashStyle != DashStyleSolid)
  768. {
  769. minWidth *= 2.0f;
  770. }
  771. }
  772. if(!NeedsToTransform)
  773. {
  774. if(MinimumWidth < minWidth)
  775. {
  776. NeedsToTransform = TRUE;
  777. StrokeWidth = minWidth;
  778. MaximumWidth = minWidth;
  779. MinimumWidth = minWidth;
  780. // Ignore the pen transform.
  781. XForm.Reset();
  782. if(matrix)
  783. XForm = *matrix;
  784. hasPenTransform = FALSE;
  785. penTrans.Reset();
  786. }
  787. }
  788. InvXForm = XForm;
  789. if(InvXForm.IsInvertible())
  790. {
  791. InvXForm.Invert();
  792. if(hasPenTransform)
  793. penTrans.Invert();
  794. }
  795. else
  796. {
  797. WARNING(("The matrix is degenerate for path widening constructor."));
  798. return;
  799. }
  800. const GpPointF* points1 = points;
  801. GpPointF pointBuffer[32];
  802. GpPointF* points2 = pointBuffer;
  803. if((hasPenTransform && !NeedsToTransform)|| (NeedsToTransform && !XForm.IsIdentity()))
  804. {
  805. if(count > 32)
  806. {
  807. points2 = (GpPointF*) GpMalloc(count*sizeof(GpPointF));
  808. }
  809. if(points2)
  810. {
  811. GpMemcpy(points2, points, count*sizeof(GpPointF));
  812. if(hasPenTransform && !NeedsToTransform)
  813. {
  814. // Apply the inverse transform of Pen.
  815. penTrans.Transform(points2, count);
  816. }
  817. else
  818. {
  819. // Transform to the device coordinates.
  820. XForm.Transform(points2, count);
  821. }
  822. points1 = points2;
  823. }
  824. else
  825. {
  826. WARNING(("Not enough memory for path widening constructor."));
  827. return;
  828. }
  829. }
  830. DpPathIterator iter(points1, types, count);
  831. if(!iter.IsValid())
  832. {
  833. if(points2 != pointBuffer)
  834. {
  835. GpFree(points2);
  836. }
  837. return;
  838. }
  839. // Make sure given poins are not degenerate.
  840. BOOL degenerate = TRUE;
  841. INT k = 1;
  842. while(degenerate && k < count)
  843. {
  844. if(points1[k-1].X != points1[k].X || points1[k-1].Y != points1[k].Y)
  845. degenerate = FALSE;
  846. k++;
  847. }
  848. if(degenerate)
  849. {
  850. if(points2 != pointBuffer)
  851. {
  852. GpFree(points2);
  853. }
  854. WARNING(("Input data is degenerate for widening."));
  855. return;
  856. }
  857. GpStatus status = Ok;
  858. INT startIndex, endIndex;
  859. BOOL isClosed;
  860. INT dataCount = 0;
  861. BYTE* ctrTypes = NULL;
  862. GpPointF* ctrPoints = NULL;
  863. GpPointF lastPt, nextPt;
  864. while(iter.NextSubpath(&startIndex, &endIndex, &isClosed) && status == Ok)
  865. {
  866. INT typeStartIndex, typeEndIndex;
  867. BYTE pathType;
  868. BOOL isFirstPoint = TRUE;
  869. while(iter.NextPathType(&pathType, &typeStartIndex, &typeEndIndex)
  870. && status == Ok)
  871. {
  872. INT segmentCount;
  873. const GpPointF* dataPoints = NULL;
  874. const BYTE* dataTypes = NULL;
  875. nextPt = points1[typeStartIndex];
  876. switch(pathType)
  877. {
  878. case PathPointTypeStart:
  879. break;
  880. case PathPointTypeBezier:
  881. // The path must be flattened before calling widen.
  882. ASSERT(FALSE);
  883. break;
  884. case PathPointTypeLine:
  885. default: // And all other types are treated as line points.
  886. // Get the data for the Line segment.
  887. segmentCount = typeEndIndex - typeStartIndex + 1;
  888. dataPoints = points1 + typeStartIndex;
  889. dataTypes = NULL;
  890. break;
  891. }
  892. if(status == Ok && pathType != PathPointTypeStart)
  893. {
  894. // Allocate memory for CenterTypes and CenterPoints.
  895. status = CenterTypes.ReserveSpace(segmentCount);
  896. if(status == Ok)
  897. status = CenterPoints.ReserveSpace(segmentCount);
  898. if(status == Ok)
  899. {
  900. ctrTypes = CenterTypes.GetDataBuffer();
  901. ctrPoints = CenterPoints.GetDataBuffer();
  902. }
  903. else
  904. {
  905. ctrTypes = NULL;
  906. ctrPoints = NULL;
  907. }
  908. if(ctrTypes && ctrPoints)
  909. {
  910. BYTE nextType;
  911. INT n = CenterTypes.GetCount();
  912. ctrTypes += n;
  913. ctrPoints += n;
  914. dataCount = 0;
  915. // Add the first point.
  916. if(isFirstPoint)
  917. {
  918. // We must check the dash mode
  919. // for the first point of the subpath.
  920. nextType = PathPointTypeStart;
  921. if(iter.IsDashMode(typeStartIndex))
  922. nextType |= PathPointTypeDashMode;
  923. else
  924. nextType &= ~PathPointTypeDashMode;
  925. *ctrTypes++ = nextType;
  926. *ctrPoints++ = nextPt;
  927. lastPt = nextPt;
  928. isFirstPoint = FALSE;
  929. dataCount++;
  930. }
  931. else
  932. {
  933. // Don't copy the first
  934. // if it is the same as the last point.
  935. if(lastPt.X != nextPt.X || lastPt.Y != nextPt.Y)
  936. {
  937. // We don't have to check dash mode
  938. // for the intermediate points.
  939. nextType = PathPointTypeLine;
  940. *ctrTypes++ = nextType;
  941. *ctrPoints++ = nextPt;
  942. lastPt = nextPt;
  943. dataCount++;
  944. }
  945. }
  946. // Add the remaining points.
  947. segmentCount--;
  948. dataPoints++;
  949. if(dataTypes)
  950. dataTypes++;
  951. INT addedCount = copyNonDegeneratePoints(
  952. pathType,
  953. ctrPoints,
  954. ctrTypes,
  955. dataPoints,
  956. dataTypes,
  957. segmentCount,
  958. &lastPt);
  959. dataCount += addedCount;
  960. CenterTypes.AdjustCount(dataCount);
  961. CenterPoints.AdjustCount(dataCount);
  962. }
  963. else
  964. status = OutOfMemory;
  965. }
  966. lastPt = points1[typeEndIndex];
  967. }
  968. if(status == Ok)
  969. {
  970. ctrTypes = CenterTypes.GetDataBuffer();
  971. dataCount = CenterTypes.GetCount();
  972. if(isClosed)
  973. ctrTypes[dataCount - 1] |= PathPointTypeCloseSubpath;
  974. else
  975. ctrTypes[dataCount - 1] &= ~PathPointTypeCloseSubpath;
  976. // We must check the dash mode for the last
  977. // point of the subpath.
  978. if(iter.IsDashMode(endIndex))
  979. ctrTypes[dataCount - 1] |= PathPointTypeDashMode;
  980. else
  981. ctrTypes[dataCount - 1] &= ~PathPointTypeDashMode;
  982. }
  983. }
  984. if(points2 != pointBuffer)
  985. {
  986. GpFree(points2);
  987. }
  988. if(status == Ok)
  989. {
  990. ctrPoints = CenterPoints.GetDataBuffer();
  991. ctrTypes = CenterTypes.GetDataBuffer();
  992. dataCount = CenterPoints.GetCount();
  993. Iterator.SetData(ctrPoints, ctrTypes, dataCount);
  994. SetValid(Iterator.IsValid());
  995. #ifdef DEBUG_PATHWIDENER
  996. if(!IsValid())
  997. {
  998. WARNING(("PathWidener is invalid."));
  999. }
  1000. #endif
  1001. }
  1002. }
  1003. /**************************************************************************\
  1004. *
  1005. * Function Description:
  1006. *
  1007. * Calculates the unit gradient vectors of points as array of
  1008. * (count + 1). All the memories must be allocated and be checked
  1009. * by the caller.
  1010. *
  1011. * The first element of the gradient is from the end point to the
  1012. * the start point. If the end point is identical to the start point,
  1013. * the previous point is used.
  1014. * The last element of the gradient is from the start point to the end
  1015. * point. If the start point is identical to the end point, the next
  1016. * point is used.
  1017. * If distances array is not NULL, this returns the distance of each
  1018. * segments.
  1019. *
  1020. * Arguments:
  1021. *
  1022. * [OUT] grad - The gradient array of (count + 1) elements.
  1023. * [OUT] distances - The distance array of (count + 1) elements or NULL.
  1024. * [IN] points - The given points of count elements.
  1025. * [IN] count - The number of given points.
  1026. *
  1027. * Return Value:
  1028. *
  1029. * Status
  1030. *
  1031. \**************************************************************************/
  1032. GpStatus
  1033. CalculateGradientArray(
  1034. GpPointF* grad,
  1035. REAL* distances,
  1036. const GpPointF* points,
  1037. INT count
  1038. )
  1039. {
  1040. GpPointF* grad1 = grad;
  1041. REAL* distances1 = distances;
  1042. const GpPointF* points1 = points;
  1043. // Go to the starting point of this subpath.
  1044. GpPointF startPt, endPt, lastPt, nextPt;
  1045. startPt = *points1;
  1046. INT i = count - 1;
  1047. BOOL different = FALSE;
  1048. points1 += i; // Go to the end point.
  1049. while(i > 0 && !different)
  1050. {
  1051. endPt = *points1--;
  1052. if(endPt.X != startPt.X || endPt.Y != startPt.Y)
  1053. different = TRUE;
  1054. i--;
  1055. }
  1056. if(!different)
  1057. {
  1058. // All points are the same.
  1059. WARNING(("Trying to calculate the gradients for degenerate points."));
  1060. return GenericError;
  1061. }
  1062. points1 = points;
  1063. lastPt = endPt;
  1064. i = 0;
  1065. while(i <= count)
  1066. {
  1067. REAL dx, dy, d;
  1068. if(i < count)
  1069. nextPt = *points1++;
  1070. else
  1071. nextPt = startPt;
  1072. dx = nextPt.X - lastPt.X;
  1073. dy = nextPt.Y - lastPt.Y;
  1074. d = dx*dx + dy*dy;
  1075. if(d > 0)
  1076. {
  1077. d = REALSQRT(d);
  1078. dx /= d;
  1079. dy /= d;
  1080. }
  1081. grad1->X = dx;
  1082. grad1->Y = dy;
  1083. // Record the distance only when the given distance array is not NULL.
  1084. if(distances)
  1085. *distances1++ = d;
  1086. grad1++;
  1087. lastPt = nextPt;
  1088. i++;
  1089. }
  1090. // Make sure the last gradient is not 0.
  1091. grad1 = grad + count;
  1092. if(grad1->X == 0 && grad1->Y == 0)
  1093. {
  1094. // The start and end point are the same. Find
  1095. // the next non-zero gradient.
  1096. i = 1;
  1097. grad1 = grad + i;
  1098. while(i < count)
  1099. {
  1100. if(grad1->X != 0 || grad1->Y != 0)
  1101. {
  1102. grad[count] = *grad1;
  1103. if(distances)
  1104. distances[count] = distances[i];
  1105. break;
  1106. }
  1107. i++;
  1108. grad1++;
  1109. }
  1110. }
  1111. return Ok;
  1112. }
  1113. /**************************************************************************\
  1114. *
  1115. * Function Description:
  1116. *
  1117. * Calculates the normal gradient of points between startIndex
  1118. * and endIndex. The last element of the gradient is from endIndex
  1119. * to startIndex. If the next point is identical to the previous point,
  1120. * the gradient is set to (0, 0).
  1121. *
  1122. * Arguments:
  1123. *
  1124. * [IN] startIndex - the starting index.
  1125. * [IN] endIndex - the ending index.
  1126. *
  1127. * Return Value:
  1128. *
  1129. * Status
  1130. *
  1131. \**************************************************************************/
  1132. GpStatus
  1133. GpPathWidener::CalculateGradients(INT startIndex, INT endIndex)
  1134. {
  1135. GpPointF* points0 = CenterPoints.GetDataBuffer();
  1136. INT count = endIndex - startIndex + 1;
  1137. if(!points0 || count <= 0)
  1138. return GenericError;
  1139. Gradients.Reset(FALSE);
  1140. GpPointF* grad = Gradients.AddMultiple(count + 1);
  1141. if(!grad)
  1142. return OutOfMemory;
  1143. GpPointF* points = points0 + startIndex;
  1144. return CalculateGradientArray(grad, NULL, points, count);
  1145. }
  1146. GpStatus
  1147. GpPathWidener::CalculateNormals(
  1148. REAL leftWidth,
  1149. REAL rightWidth
  1150. )
  1151. {
  1152. NeedsToAdjustNormals = FALSE;
  1153. INT count = Gradients.GetCount();
  1154. GpPointF* grad0 = Gradients.GetDataBuffer();
  1155. if(count <= 0)
  1156. {
  1157. WARNING(("Gradients must be calculated\n"
  1158. "before the normals are calculated."));
  1159. return GenericError;
  1160. }
  1161. Normals.Reset(FALSE);
  1162. GpPointF* norm0 = Normals.AddMultiple(count);
  1163. if(!norm0)
  1164. return OutOfMemory;
  1165. GpPointF* norm = norm0;
  1166. GpPointF* grad = grad0;
  1167. // Calculate the left normals.
  1168. INT i;
  1169. for(i = 0; i < count; i++)
  1170. {
  1171. norm->X = grad->Y;
  1172. norm->Y = - grad->X;
  1173. norm++;
  1174. grad++;
  1175. }
  1176. if(IsAntiAliased)
  1177. return Ok;
  1178. // Check if the minimum width is less than 1.0.
  1179. REAL width = REALABS(leftWidth - rightWidth);
  1180. if(width*MinimumWidth >= 1.0f)
  1181. return Ok;
  1182. NeedsToAdjustNormals = TRUE;
  1183. if(!NeedsToTransform)
  1184. {
  1185. // Transform to the device space.
  1186. // When NeedsToTransform is TRUE, the gradient is already
  1187. // calculated in the device coordinates.
  1188. if(!XForm.IsIdentity())
  1189. XForm.VectorTransform(norm0, count);
  1190. }
  1191. // Set the minimum line width to be just over 1.0
  1192. REAL criteria = 1.00005f;
  1193. REAL value;
  1194. if(width > 0)
  1195. value = criteria/width;
  1196. else
  1197. value = criteria*MinimumWidth/1.0f;
  1198. norm = norm0;
  1199. for(i = 0; i < count; i++)
  1200. {
  1201. REAL xx = REALABS(norm->X);
  1202. REAL yy = REALABS(norm->Y);
  1203. if(xx >= yy)
  1204. {
  1205. if(width*xx < criteria)
  1206. {
  1207. if(norm->X >= 0)
  1208. norm->X = value;
  1209. else
  1210. norm->X = - value;
  1211. norm->Y = 0;
  1212. }
  1213. }
  1214. else
  1215. {
  1216. if(width*yy < criteria)
  1217. {
  1218. if(norm->Y >= 0)
  1219. norm->Y = value;
  1220. else
  1221. norm->Y = - value;
  1222. norm->X = 0;
  1223. }
  1224. }
  1225. norm++;
  1226. }
  1227. if(!NeedsToTransform)
  1228. {
  1229. // Transform back to the world space in case of
  1230. // a non fixed width pen.
  1231. if(!InvXForm.IsIdentity())
  1232. InvXForm.VectorTransform(norm0, count);
  1233. }
  1234. return Ok;
  1235. }
  1236. GpStatus
  1237. GpPathWidener::Widen(GpPath **path)
  1238. {
  1239. // Must be a pointer to a path pointer that is NULL.
  1240. ASSERT(path != NULL);
  1241. ASSERT(*path == NULL);
  1242. GpStatus status = Ok;
  1243. // Get the widened path points and types into the
  1244. // dynarray objects.
  1245. DynPointFArray widenedPoints;
  1246. DynByteArray widenedTypes;
  1247. status = Widen(
  1248. &widenedPoints,
  1249. &widenedTypes
  1250. );
  1251. if(status != Ok) { return status; }
  1252. // Remove the internal flag.
  1253. INT pathCount = widenedTypes.GetCount();
  1254. BYTE* pathTypes = widenedTypes.GetDataBuffer();
  1255. for(INT i = 0; i < pathCount; i++, pathTypes++)
  1256. {
  1257. if(*pathTypes & PathPointTypeInternalUse)
  1258. {
  1259. *pathTypes &= ~PathPointTypeInternalUse;
  1260. }
  1261. }
  1262. // If everything worked, create a new path
  1263. // from the widened points.
  1264. if(status == Ok)
  1265. {
  1266. *path = new GpPath(
  1267. widenedPoints.GetDataBuffer(),
  1268. widenedTypes.GetDataBuffer(),
  1269. widenedPoints.GetCount(),
  1270. FillModeWinding
  1271. );
  1272. if(*path == NULL) { status = OutOfMemory; }
  1273. }
  1274. return status;
  1275. }
  1276. GpStatus
  1277. GpPathWidener::Widen(
  1278. DynPointFArray* widenedPoints,
  1279. DynByteArray* widenedTypes
  1280. )
  1281. {
  1282. FPUStateSaver::AssertMode();
  1283. if(!IsValid() || !widenedPoints || !widenedTypes)
  1284. {
  1285. return InvalidParameter;
  1286. }
  1287. const GpPointF* centerPoints = CenterPoints.GetDataBuffer();
  1288. const BYTE* centerTypes = CenterTypes.GetDataBuffer();
  1289. INT centerCount = CenterPoints.GetCount();
  1290. INT startIndex, endIndex;
  1291. BOOL isClosed;
  1292. GpStatus status = Ok;
  1293. DynPointFArray customStartCapPoints;
  1294. DynPointFArray customEndCapPoints;
  1295. DynByteArray customStartCapTypes;
  1296. DynByteArray customEndCapTypes;
  1297. // Clear the data in widenedPoints and widenedTypes.
  1298. widenedPoints->Reset(FALSE);
  1299. widenedTypes->Reset(FALSE);
  1300. REAL width = OriginalStrokeWidth;
  1301. INT compoundCount = Pen->CompoundCount;
  1302. REAL* compoundArray = NULL;
  1303. INT cpCount;
  1304. if(compoundCount > 0)
  1305. cpCount = compoundCount;
  1306. else
  1307. cpCount = 2;
  1308. REAL compoundArray0[8];
  1309. if(cpCount <= 8)
  1310. compoundArray = &compoundArray0[0];
  1311. else
  1312. compoundArray = (REAL*) GpMalloc(cpCount*sizeof(REAL));
  1313. INT kk;
  1314. if(compoundArray)
  1315. {
  1316. // Don't attempt to draw a compound line that is empty, or is aliased and has
  1317. // more line components than the width of the line. It can be rounded out of
  1318. // existance
  1319. if(compoundCount > 0 &&
  1320. (IsAntiAliased || (compoundCount / 2) <= (width * UnitScale)))
  1321. {
  1322. // Don't attempt to draw a compound line that is less than 0.5 device
  1323. // units in width. These can disappear when rasterized, depending on
  1324. // what Y coordinate they fall on.
  1325. if ((UnitScale * width) >= 0.5f)
  1326. {
  1327. GpMemcpy(compoundArray, Pen->CompoundArray, compoundCount*sizeof(REAL));
  1328. for(kk = 0; kk < compoundCount; kk++)
  1329. {
  1330. compoundArray[kk] *= width;
  1331. }
  1332. }
  1333. else
  1334. {
  1335. compoundArray[0] = 0;
  1336. compoundArray[1] = 0.5f;
  1337. if (cpCount > 2)
  1338. {
  1339. cpCount = 2;
  1340. }
  1341. }
  1342. }
  1343. else
  1344. {
  1345. compoundArray[0] = 0;
  1346. compoundArray[1] = StrokeWidth;
  1347. if (cpCount > 2)
  1348. {
  1349. cpCount = 2;
  1350. }
  1351. }
  1352. }
  1353. else
  1354. {
  1355. SetValid(FALSE);
  1356. return OutOfMemory;
  1357. }
  1358. REAL left0, right0;
  1359. BOOL isCenteredPen = FALSE;
  1360. BOOL needsToFlip = FALSE;
  1361. if(Pen->PenAlignment == PenAlignmentInset)
  1362. {
  1363. // Check if the coordinates are flipped.
  1364. // If the determinant of the transform matrix is negative,
  1365. // the coordinate system is flipped.
  1366. if(XForm.IsInvertible() && XForm.GetDeterminant() < 0)
  1367. needsToFlip = TRUE;
  1368. }
  1369. // OriginalStrokeWidth is required for the compound lines, but we're
  1370. // widening now and StrokeWidth == max(OriginalStrokeWidth, MinimumWidth)
  1371. // which is the value we need to widen at in order to avoid dropping out
  1372. // lines.
  1373. width = StrokeWidth;
  1374. switch(Pen->PenAlignment)
  1375. {
  1376. case PenAlignmentInset:
  1377. if(!needsToFlip)
  1378. left0 = 0; // Same as right align.
  1379. else
  1380. left0 = width; // Same as left align.
  1381. break;
  1382. case PenAlignmentCenter:
  1383. default:
  1384. left0 = width/2;
  1385. isCenteredPen = TRUE;
  1386. }
  1387. right0 = left0 - width;
  1388. REAL startInset0 = 0, endInset0 = 0;
  1389. GpCustomLineCap* customStartCap = NULL;
  1390. GpCustomLineCap* customEndCap = NULL;
  1391. while(Iterator.NextSubpath(&startIndex, &endIndex, &isClosed)
  1392. && status == Ok)
  1393. {
  1394. GpLineCap startCap = Pen->StartCap;
  1395. GpLineCap endCap = Pen->EndCap;
  1396. BYTE startType = centerTypes[startIndex];
  1397. BYTE endType = centerTypes[endIndex];
  1398. GpLineCapMode startCapMode = LineCapDefaultMode;
  1399. GpLineCapMode endCapMode = LineCapDefaultMode;
  1400. if(startType & PathPointTypeDashMode)
  1401. {
  1402. startCap = Pen->DashCap;
  1403. startCapMode = LineCapDashMode;
  1404. }
  1405. else
  1406. {
  1407. // If the start cap is one of the anchor caps, default the widener
  1408. // to using the dashcap for the startCap.
  1409. if(((startCap & LineCapAnchorMask) != 0) ||
  1410. (startCap == LineCapCustom))
  1411. {
  1412. startCap = Pen->DashCap;
  1413. }
  1414. }
  1415. if(endType & PathPointTypeDashMode)
  1416. {
  1417. endCap = Pen->DashCap;
  1418. endCapMode = LineCapDashMode;
  1419. }
  1420. else
  1421. {
  1422. // If the end cap is one of the anchor caps, default the widener
  1423. // to using the dashcap for the endCap.
  1424. if(((endCap & LineCapAnchorMask) != 0) ||
  1425. (endCap == LineCapCustom))
  1426. {
  1427. endCap = Pen->DashCap;
  1428. }
  1429. }
  1430. if(InsetPenMode)
  1431. {
  1432. // Inset pen only supports these caps.
  1433. if(endCap != LineCapRound && endCap != LineCapFlat)
  1434. {
  1435. endCap = LineCapFlat;
  1436. }
  1437. if(startCap != LineCapRound && startCap != LineCapFlat)
  1438. {
  1439. startCap = LineCapFlat;
  1440. }
  1441. }
  1442. Inset1 = Inset2 = 0.0f;
  1443. if(startCap == LineCapSquare)
  1444. {
  1445. Inset1 = -0.5f*StrokeWidth;
  1446. }
  1447. if(endCap == LineCapSquare)
  1448. {
  1449. Inset2 = -0.5f*StrokeWidth;
  1450. }
  1451. status = CalculateGradients(startIndex, endIndex);
  1452. kk = 0;
  1453. BOOL isCompoundLine = FALSE;
  1454. GpLineCap startCap1 = startCap;
  1455. GpLineCap endCap1 = endCap;
  1456. if(cpCount > 2)
  1457. {
  1458. // Don't add the caps for the individual
  1459. // compound line.
  1460. isCompoundLine = TRUE;
  1461. startCap1 = LineCapFlat;
  1462. endCap1 = LineCapFlat;
  1463. }
  1464. while(kk < cpCount && status == Ok)
  1465. {
  1466. REAL leftWidth = left0 - compoundArray[kk];
  1467. REAL rightWidth = left0 - compoundArray[kk + 1];
  1468. if(REALABS(leftWidth-rightWidth)>REAL_EPSILON)
  1469. {
  1470. status = CalculateNormals(leftWidth, rightWidth);
  1471. if(status != Ok) { break; }
  1472. // Check if we can use the Bevel join for inside lines.
  1473. BOOL useBevelJoinInside = isCenteredPen && !isCompoundLine;
  1474. if(USE_POLYGON_JOIN)
  1475. {
  1476. status = SetPolygonJoin(leftWidth, rightWidth, FALSE);
  1477. }
  1478. status = WidenSubpath(
  1479. widenedPoints,
  1480. widenedTypes,
  1481. leftWidth,
  1482. rightWidth,
  1483. startIndex,
  1484. endIndex,
  1485. isClosed,
  1486. startCap1,
  1487. endCap1,
  1488. useBevelJoinInside
  1489. );
  1490. Iterator.RewindSubpath();
  1491. }
  1492. kk += 2;
  1493. }
  1494. // Add the compound line caps if necessary.
  1495. if(status == Ok && isCompoundLine && !isClosed)
  1496. {
  1497. status = AddCompoundCaps(
  1498. widenedPoints,
  1499. widenedTypes,
  1500. left0,
  1501. right0,
  1502. startIndex,
  1503. endIndex,
  1504. startCap,
  1505. endCap
  1506. );
  1507. }
  1508. }
  1509. if(status != Ok) { return status; }
  1510. GpPointF* pts;
  1511. INT count;
  1512. if(!NeedsToTransform)
  1513. {
  1514. GpMatrix penTrans = ((DpPen*) Pen)->Xform;
  1515. BOOL hasPenTransform = FALSE;
  1516. if(!penTrans.IsTranslate())
  1517. {
  1518. hasPenTransform = TRUE;
  1519. penTrans.RemoveTranslation();
  1520. pts = widenedPoints->GetDataBuffer();
  1521. count = widenedPoints->GetCount();
  1522. penTrans.Transform(pts, count);
  1523. }
  1524. }
  1525. else if(!InvXForm.IsIdentity())
  1526. {
  1527. // Case of the Fixed width pen.
  1528. pts = widenedPoints->GetDataBuffer();
  1529. count = widenedPoints->GetCount();
  1530. InvXForm.Transform(pts, count);
  1531. }
  1532. #ifdef DEBUG_PATHWIDENER
  1533. if(status == Ok)
  1534. {
  1535. DpPathTypeIterator iter2(widenedTypes->GetDataBuffer(),
  1536. widenedTypes->GetCount());
  1537. if(!iter2.IsValid())
  1538. {
  1539. WARNING(("Widening result is not valid."));
  1540. status = GenericError;
  1541. }
  1542. }
  1543. #endif
  1544. if(status == Ok)
  1545. SetValid(TRUE);
  1546. if(compoundArray != &compoundArray0[0])
  1547. GpFree(compoundArray);
  1548. return status;
  1549. }
  1550. GpStatus
  1551. GpPathWidener::WidenSubpath(
  1552. DynPointFArray* widenedPoints,
  1553. DynByteArray* widenedTypes,
  1554. REAL leftWidth,
  1555. REAL rightWidth,
  1556. INT startIndex,
  1557. INT endIndex,
  1558. BOOL isClosed,
  1559. GpLineCap startCap,
  1560. GpLineCap endCap,
  1561. BOOL useBevelJoinInside
  1562. )
  1563. {
  1564. const GpPointF* centerPoints = CenterPoints.GetDataBuffer();
  1565. const BYTE* centerTypes = CenterTypes.GetDataBuffer();
  1566. INT centerCount = CenterPoints.GetCount();
  1567. GpLineJoin lineJoin = Pen->Join;
  1568. REAL miterLimit2 = Pen->MiterLimit;
  1569. miterLimit2 *= miterLimit2;
  1570. INT typeStartIndex, typeEndIndex;
  1571. BYTE pathType;
  1572. BOOL isFirstType = TRUE;
  1573. BOOL isLastType = FALSE;
  1574. BOOL isLastPointSame;
  1575. GpPointF startPt, endPt;
  1576. startPt = centerPoints[startIndex];
  1577. endPt = centerPoints[endIndex];
  1578. if(startPt.X != endPt.X || startPt.Y != endPt.Y)
  1579. isLastPointSame = FALSE;
  1580. else
  1581. isLastPointSame = TRUE;
  1582. // Reset the left and right buffers.
  1583. LeftTypes.Reset(FALSE);
  1584. LeftPoints.Reset(FALSE);
  1585. RightTypes.Reset(FALSE);
  1586. RightPoints.Reset(FALSE);
  1587. INT subpathCount = endIndex - startIndex + 1;
  1588. INT joinMultiplier = 2;
  1589. if(lineJoin == LineJoinRound)
  1590. joinMultiplier = 7;
  1591. GpStatus status = LeftTypes.ReserveSpace(joinMultiplier*subpathCount);
  1592. if(status == Ok)
  1593. status = LeftPoints.ReserveSpace(joinMultiplier*subpathCount);
  1594. if(status == Ok)
  1595. status = RightTypes.ReserveSpace(joinMultiplier*subpathCount);
  1596. if(status == Ok)
  1597. status = RightPoints.ReserveSpace(joinMultiplier*subpathCount);
  1598. if(status != Ok)
  1599. return status;
  1600. // Get Gradient data buffer.
  1601. GpPointF *grad0, *norm0;
  1602. grad0 = Gradients.GetDataBuffer();
  1603. norm0 = Normals.GetDataBuffer();
  1604. // Get Left and Right data buffers.
  1605. GpPointF* leftPoints0 = LeftPoints.GetDataBuffer();
  1606. BYTE* leftTypes0 = LeftTypes.GetDataBuffer();
  1607. GpPointF* rightPoints0 = RightPoints.GetDataBuffer();
  1608. BYTE* rightTypes0 = RightTypes.GetDataBuffer();
  1609. GpPointF lastPt, nextPt;
  1610. GpPointF leftEndPt, rightEndPt;
  1611. INT leftCount = 0, rightCount = 0;
  1612. INT flag = 0;
  1613. if(isClosed)
  1614. flag |= WideningClosed;
  1615. if(isFirstType)
  1616. flag |= WideningFirstType;
  1617. if(isLastType)
  1618. flag |= WideningLastType;
  1619. if(isLastPointSame)
  1620. flag |= WideningLastPointSame;
  1621. if(NeedsToAdjustNormals)
  1622. flag |= WideningNeedsToAdjustNormals;
  1623. if(useBevelJoinInside)
  1624. flag |= WideningUseBevelJoinInside;
  1625. const GpPointF* dataPoints = centerPoints + startIndex;
  1626. INT dataCount = endIndex - startIndex + 1;
  1627. REAL firstInsets[2], lastInsets[2];
  1628. // Never inset more than the length of the line for the first inset, and
  1629. // never more than the amount left on the line after the first inset
  1630. // has been applied. This can result in odd endcaps and dashcaps when you
  1631. // have a line that ends in the middle of a short dash segment.
  1632. REAL linelength = REALSQRT(
  1633. distance_squared(
  1634. centerPoints[startIndex],
  1635. centerPoints[endIndex]
  1636. )
  1637. );
  1638. firstInsets[0] = min(Inset1, linelength);
  1639. firstInsets[1] = min(Inset1, linelength);
  1640. lastInsets[0] = min(Inset2, linelength-firstInsets[0]);
  1641. lastInsets[1] = min(Inset2, linelength-firstInsets[1]);
  1642. WidenFirstPoint(
  1643. leftWidth,
  1644. rightWidth,
  1645. lineJoin,
  1646. miterLimit2,
  1647. leftPoints0,
  1648. leftTypes0,
  1649. &leftCount,
  1650. rightPoints0,
  1651. rightTypes0,
  1652. &rightCount,
  1653. &leftEndPt,
  1654. &rightEndPt,
  1655. grad0,
  1656. norm0,
  1657. dataPoints,
  1658. dataCount,
  1659. &lastPt,
  1660. &firstInsets[0],
  1661. flag
  1662. );
  1663. // Iterate through all subtypes in the current subpath.
  1664. while(Iterator.NextPathType(&pathType, &typeStartIndex, &typeEndIndex)
  1665. && status == Ok)
  1666. {
  1667. // Offset index from the current subpath.
  1668. INT offsetIndex = typeStartIndex - startIndex;
  1669. GpPointF* grad = grad0 + offsetIndex;
  1670. GpPointF* norm = norm0 + offsetIndex;
  1671. // Get the starting data buffers of the current subtypes.
  1672. dataPoints = centerPoints + typeStartIndex;
  1673. dataCount = typeEndIndex - typeStartIndex + 1;
  1674. // Get the starting buffers for the left and right data.
  1675. GpPointF* leftPoints = leftPoints0 + leftCount;
  1676. BYTE* leftTypes = leftTypes0 + leftCount;
  1677. GpPointF* rightPoints = rightPoints0 + rightCount;
  1678. BYTE* rightTypes = rightTypes0 + rightCount;
  1679. INT addedLeftCount = 0, addedRightCount = 0;
  1680. if(pathType != PathPointTypeStart)
  1681. {
  1682. if(typeEndIndex == endIndex)
  1683. isLastType = TRUE;
  1684. flag = 0;
  1685. if(isClosed)
  1686. flag |= WideningClosed;
  1687. if(isFirstType)
  1688. flag |= WideningFirstType;
  1689. if(isLastType)
  1690. flag |= WideningLastType;
  1691. if(isLastPointSame)
  1692. flag |= WideningLastPointSame;
  1693. if(NeedsToAdjustNormals)
  1694. flag |= WideningNeedsToAdjustNormals;
  1695. if(useBevelJoinInside)
  1696. flag |= WideningUseBevelJoinInside;
  1697. status = WidenEachPathType(
  1698. pathType,
  1699. leftWidth,
  1700. rightWidth,
  1701. lineJoin,
  1702. miterLimit2,
  1703. leftPoints,
  1704. leftTypes,
  1705. &addedLeftCount,
  1706. rightPoints,
  1707. rightTypes,
  1708. &addedRightCount,
  1709. grad,
  1710. norm,
  1711. dataPoints,
  1712. dataCount,
  1713. &lastPt,
  1714. &lastInsets[0],
  1715. flag
  1716. );
  1717. leftCount += addedLeftCount;
  1718. rightCount += addedRightCount;
  1719. if(isFirstType && (leftCount > 0 || rightCount > 0))
  1720. isFirstType = FALSE;
  1721. }
  1722. lastPt = centerPoints[typeEndIndex];
  1723. }
  1724. if(status == Ok)
  1725. {
  1726. LeftTypes.SetCount(leftCount);
  1727. LeftPoints.SetCount(leftCount);
  1728. RightTypes.SetCount(rightCount);
  1729. RightPoints.SetCount(rightCount);
  1730. }
  1731. else
  1732. return status;
  1733. GpPointF startPoint, endPoint;
  1734. GpPointF startGrad, endGrad;
  1735. GpPointF startNorm, endNorm;
  1736. startPoint = *(centerPoints + startIndex);
  1737. endPoint = *(centerPoints + endIndex);
  1738. startGrad = grad0[1];
  1739. endGrad = grad0[endIndex - startIndex];
  1740. startNorm = norm0[1];
  1741. endNorm = norm0[endIndex - startIndex];
  1742. status = SetCaps(
  1743. startCap,
  1744. endCap,
  1745. startPoint,
  1746. startGrad,
  1747. startNorm,
  1748. endPoint,
  1749. endGrad,
  1750. endNorm,
  1751. leftWidth,
  1752. rightWidth,
  1753. centerPoints + startIndex,
  1754. endIndex - startIndex + 1
  1755. );
  1756. status = CombineSubpathOutlines(
  1757. widenedPoints,
  1758. widenedTypes,
  1759. isClosed
  1760. );
  1761. return status;
  1762. }
  1763. GpStatus
  1764. GpPathWidener::SetPolygonJoin(
  1765. REAL leftWidth,
  1766. REAL rightWidth,
  1767. BOOL isAntialiased
  1768. )
  1769. {
  1770. // This codes is intended to non-pen transform and in WorldUnit for now.
  1771. REAL minimumWidth = MinimumWidth;
  1772. if(leftWidth - rightWidth < StrokeWidth)
  1773. minimumWidth = (leftWidth - rightWidth)/StrokeWidth;
  1774. const INT maxPolyCount = 8;
  1775. INT count = 0;
  1776. GpPointF points[maxPolyCount];
  1777. REAL grads[maxPolyCount];
  1778. JoinPolygonPoints.Reset(FALSE);
  1779. JoinPolygonAngles.Reset(FALSE);
  1780. // Define Hobby's polygon.
  1781. if(minimumWidth < 1.06)
  1782. {
  1783. count = 4;
  1784. points[0].X = 0.0f; points[0].Y = -0.5f;
  1785. points[1].X = 0.5f; points[1].Y = 0.0f;
  1786. points[2].X = 0.0f; points[2].Y = 0.5f;
  1787. points[3].X = -0.5f; points[3].Y = 0.0f;
  1788. }
  1789. else if(minimumWidth < 1.5)
  1790. {
  1791. count = 4;
  1792. points[0].X = -0.5f; points[0].Y = -0.5f;
  1793. points[1].X = 0.5f; points[1].Y = -0.5f;
  1794. points[2].X = 0.5f; points[2].Y = 0.5f;
  1795. points[3].X = -0.5f; points[3].Y = 0.5f;
  1796. }
  1797. else if(minimumWidth < 1.77)
  1798. {
  1799. count = 4;
  1800. points[0].X = 0.0f; points[0].Y = -1.0f;
  1801. points[1].X = 1.0f; points[1].Y = 0.0f;
  1802. points[2].X = 0.0f; points[2].Y = 1.0f;
  1803. points[3].X = -1.0f; points[3].Y = 0.0f;
  1804. }
  1805. else if(minimumWidth < 2.02)
  1806. {
  1807. count = 6;
  1808. points[0].X = -0.5f; points[0].Y = -1.0f;
  1809. points[1].X = 0.5f; points[1].Y = -1.0f;
  1810. points[2].X = 1.0f; points[2].Y = 0.0f;
  1811. points[3].X = 0.5f; points[3].Y = 1.0f;
  1812. points[4].X = -0.5f; points[4].Y = 1.0f;
  1813. points[5].X = -1.0f; points[5].Y = 0.0f;
  1814. }
  1815. else if(minimumWidth < 2.48)
  1816. {
  1817. count = 8;
  1818. points[0].X = -0.5f; points[0].Y = -1.0f;
  1819. points[1].X = 0.5f; points[1].Y = -1.0f;
  1820. points[2].X = 1.0f; points[2].Y = -0.5f;
  1821. points[3].X = 1.0f; points[3].Y = 0.5f;
  1822. points[4].X = 0.5f; points[4].Y = 1.0f;
  1823. points[5].X = -0.5f; points[5].Y = 1.0f;
  1824. points[6].X = -1.0f; points[6].Y = 0.5f;
  1825. points[7].X = -1.0f; points[7].Y = -0.5f;
  1826. }
  1827. else if(minimumWidth < 2.5)
  1828. {
  1829. count = 4;
  1830. points[0].X = -1.0f; points[0].Y = -1.0f;
  1831. points[1].X = 1.0f; points[1].Y = -1.0f;
  1832. points[2].X = 1.0f; points[2].Y = 1.0f;
  1833. points[3].X = -1.0f; points[3].Y = 1.0f;
  1834. }
  1835. else if(minimumWidth < 2.91)
  1836. {
  1837. count = 8;
  1838. points[0].X = 0.0f; points[0].Y = -1.5f;
  1839. points[1].X = 1.0f; points[1].Y = -1.0f;
  1840. points[2].X = 1.5f; points[2].Y = 0.0f;
  1841. points[3].X = 1.0f; points[3].Y = 1.0f;
  1842. points[4].X = 0.0f; points[4].Y = 1.5f;
  1843. points[5].X = -1.0f; points[5].Y = 1.0f;
  1844. points[6].X = -1.5f; points[6].Y = 0.0f;
  1845. points[7].X = -1.0f; points[7].Y = -1.0f;
  1846. }
  1847. else
  1848. count = 0;
  1849. if(count > 0)
  1850. {
  1851. GpPointF dP;
  1852. for(INT i = 0; i < count - 1; i++)
  1853. {
  1854. dP = points[i + 1] - points[i];
  1855. GetFastAngle(&grads[i], dP);
  1856. }
  1857. dP = points[0] - points[count - 1];
  1858. GetFastAngle(&grads[count - 1], dP);
  1859. REAL lastAngle = grads[0];
  1860. REAL nextAngle;
  1861. /*
  1862. // Find out the smallest gradient.
  1863. INT i0 = 0;
  1864. for(i = 1; i < count; i++)
  1865. {
  1866. nextAngle = grads[i];
  1867. if(nextAngle < lastAngle)
  1868. i0 = i;
  1869. lastAngle = nextAngle;
  1870. }
  1871. // Rearrange so that the polygon starts with the smallest
  1872. // gradient.
  1873. if(i0 > 1)
  1874. {
  1875. GpPointF tempPointsBuff[maxPolyCount];
  1876. REAL tempGradsBuff[maxPolyCount];
  1877. GpMemcpy(&tempPointsBuff[0], &points[0], i0*sizeof(GpPointF));
  1878. GpMemcpy(&tempGradsBuff[0], &grads[0], i0);
  1879. GpMemcpy(&points[0],
  1880. &points[i0], (count - i0)*sizeof(GpPointF));
  1881. GpMemcpy(&grads[0], &grads[i0], count - i0);
  1882. GpMemcpy(&points[count - i0], &tempPointsBuff[0],
  1883. i0*sizeof(GpPointF));
  1884. GpMemcpy(&grads[count - i0], &tempGradsBuff[0], i0);
  1885. }
  1886. */
  1887. BOOL monotonic = TRUE;
  1888. i = 1;
  1889. lastAngle = grads[0];
  1890. while(monotonic && i < count)
  1891. {
  1892. nextAngle = grads[i];
  1893. if(nextAngle < lastAngle)
  1894. monotonic = FALSE;
  1895. i++;
  1896. lastAngle = nextAngle;
  1897. }
  1898. ASSERTMSG(monotonic, ("Polygon for join is not concave."));
  1899. }
  1900. if(count > 0)
  1901. {
  1902. JoinPolygonPoints.AddMultiple(&points[0], count);
  1903. JoinPolygonAngles.AddMultiple(&grads[0], count);
  1904. }
  1905. return Ok;
  1906. }
  1907. INT getVertexID(const GpPointF& vector, BOOL forLeftEdge, INT count, const REAL* grads)
  1908. {
  1909. INT left, right, middle;
  1910. REAL angle = 0.0f;
  1911. GetFastAngle(&angle, vector);
  1912. if(!forLeftEdge)
  1913. {
  1914. angle += 4;
  1915. if(angle >= 8)
  1916. angle -= 8;
  1917. }
  1918. if(angle <= grads[0])
  1919. return 0;
  1920. if(angle >= grads[count - 1])
  1921. return count - 1;
  1922. INT i = 1;
  1923. while(angle >= grads[i] && i < count)
  1924. {
  1925. i++;
  1926. }
  1927. return i - 1;
  1928. }
  1929. GpStatus
  1930. GpPathWidener::AddCompoundCaps(
  1931. DynPointFArray* widenedPoints,
  1932. DynByteArray* widenedTypes,
  1933. REAL leftWidth,
  1934. REAL rightWidth,
  1935. INT startIndex,
  1936. INT endIndex,
  1937. GpLineCap startCap,
  1938. GpLineCap endCap
  1939. )
  1940. {
  1941. const GpPointF* centerPoints = CenterPoints.GetDataBuffer();
  1942. const BYTE* centerTypes = CenterTypes.GetDataBuffer();
  1943. INT centerCount = CenterPoints.GetCount();
  1944. const GpPointF* grad0 = Gradients.GetDataBuffer();
  1945. const GpPointF* norm0 = Normals.GetDataBuffer();
  1946. GpPointF startPoint, endPoint;
  1947. GpPointF startGrad, endGrad;
  1948. GpPointF startNorm, endNorm;
  1949. startPoint = *(centerPoints + startIndex);
  1950. endPoint = *(centerPoints + endIndex);
  1951. startGrad = grad0[1];
  1952. endGrad = grad0[endIndex - startIndex];
  1953. startNorm = norm0[1];
  1954. endNorm = norm0[endIndex - startIndex];
  1955. GpStatus status;
  1956. status = SetCaps(
  1957. startCap,
  1958. endCap,
  1959. startPoint,
  1960. startGrad,
  1961. startNorm,
  1962. endPoint,
  1963. endGrad,
  1964. endNorm,
  1965. leftWidth,
  1966. rightWidth,
  1967. centerPoints + startIndex,
  1968. endIndex - startIndex + 1
  1969. );
  1970. status = CombineClosedCaps(
  1971. widenedPoints,
  1972. widenedTypes,
  1973. &CapPoints1,
  1974. &CapPoints2,
  1975. &CapTypes1,
  1976. &CapTypes2
  1977. );
  1978. return status;
  1979. }
  1980. GpStatus
  1981. GpPathWidener::SetCaps(
  1982. GpLineCap startCap,
  1983. GpLineCap endCap,
  1984. const GpPointF& startPoint,
  1985. const GpPointF& startGrad,
  1986. const GpPointF& startNorm,
  1987. const GpPointF& endPoint,
  1988. const GpPointF& endGrad,
  1989. const GpPointF& endNorm,
  1990. REAL leftWidth,
  1991. REAL rightWidth,
  1992. const GpPointF *points,
  1993. INT pointCount
  1994. )
  1995. {
  1996. GpStatus status = Ok;
  1997. CapPoints1.Reset(FALSE);
  1998. CapTypes1.Reset(FALSE);
  1999. CapPoints2.Reset(FALSE);
  2000. CapTypes2.Reset(FALSE);
  2001. switch(startCap)
  2002. {
  2003. case LineCapRound:
  2004. if(InsetPenMode)
  2005. {
  2006. status = SetDoubleRoundCap(
  2007. startPoint,
  2008. startGrad,
  2009. TRUE,
  2010. leftWidth,
  2011. rightWidth
  2012. );
  2013. }
  2014. else
  2015. {
  2016. status = SetRoundCap(
  2017. startPoint,
  2018. startGrad,
  2019. TRUE,
  2020. leftWidth,
  2021. rightWidth
  2022. );
  2023. }
  2024. break;
  2025. case LineCapTriangle:
  2026. ASSERT(!InsetPenMode);
  2027. status = SetTriangleCap(startPoint, startGrad, TRUE, leftWidth, rightWidth, points, pointCount);
  2028. break;
  2029. default:
  2030. // Flat cap.
  2031. break;
  2032. }
  2033. switch(endCap)
  2034. {
  2035. case LineCapRound:
  2036. if(InsetPenMode)
  2037. {
  2038. status = SetDoubleRoundCap(
  2039. endPoint,
  2040. endGrad,
  2041. FALSE,
  2042. leftWidth,
  2043. rightWidth
  2044. );
  2045. }
  2046. else
  2047. {
  2048. status = SetRoundCap(
  2049. endPoint,
  2050. endGrad,
  2051. FALSE,
  2052. leftWidth,
  2053. rightWidth
  2054. );
  2055. }
  2056. break;
  2057. case LineCapTriangle:
  2058. ASSERT(!InsetPenMode);
  2059. status = SetTriangleCap(endPoint, endGrad, FALSE, leftWidth, rightWidth, points, pointCount);
  2060. break;
  2061. default:
  2062. // Flat cap.
  2063. break;
  2064. }
  2065. return status;
  2066. }
  2067. VOID modifyEdges(
  2068. GpPointF* leftPoints,
  2069. BYTE* leftTypes,
  2070. INT* leftCount,
  2071. INT* leftOffset,
  2072. GpPointF* rightPoints,
  2073. BYTE* rightTypes,
  2074. INT* rightCount,
  2075. INT* rightOffset,
  2076. GpPointF* grad,
  2077. INT gradCount
  2078. )
  2079. {
  2080. INT leftOffset1 = 0;
  2081. INT rightOffset1 = 0;
  2082. INT leftCount0 = *leftCount;
  2083. INT rightCount0 = *rightCount;
  2084. INT leftCount1 = leftCount0;
  2085. INT rightCount1 = rightCount0;
  2086. if(gradCount > 2)
  2087. {
  2088. GpPointF firstGrad = grad[1];
  2089. GpPointF lastGrad = grad[gradCount - 2];
  2090. GpPointF dP;
  2091. if(leftCount0 > 2)
  2092. {
  2093. dP.X = leftPoints[1].X - leftPoints[0].X;
  2094. dP.Y = leftPoints[1].Y - leftPoints[0].Y;
  2095. if(dP.X*firstGrad.X + dP.Y*firstGrad.Y < 0)
  2096. {
  2097. leftPoints[0] = leftPoints[1];
  2098. }
  2099. dP.X = leftPoints[leftCount0 - 1].X
  2100. - leftPoints[leftCount0 - 2].X;
  2101. dP.Y = leftPoints[leftCount0 - 1].Y
  2102. - leftPoints[leftCount0 - 2].Y;
  2103. if(dP.X*lastGrad.X + dP.Y*lastGrad.Y < 0)
  2104. {
  2105. leftPoints[leftCount0 - 1]
  2106. = leftPoints[leftCount0 - 2];
  2107. }
  2108. }
  2109. if(rightCount0 > 2)
  2110. {
  2111. dP.X = rightPoints[1].X - rightPoints[0].X;
  2112. dP.Y = rightPoints[1].Y - rightPoints[0].Y;
  2113. if(dP.X*firstGrad.X + dP.Y*firstGrad.Y < 0)
  2114. {
  2115. rightPoints[0] = rightPoints[1];
  2116. }
  2117. dP.X = rightPoints[rightCount0 - 1].X
  2118. - rightPoints[rightCount0 - 2].X;
  2119. dP.Y = rightPoints[rightCount0 - 1].Y
  2120. - rightPoints[rightCount0 - 2].Y;
  2121. if(dP.X*lastGrad.X + dP.Y*lastGrad.Y < 0)
  2122. {
  2123. rightPoints[rightCount0 - 1]
  2124. = rightPoints[rightCount0 - 2];
  2125. }
  2126. }
  2127. }
  2128. *leftCount = leftCount1;
  2129. *leftOffset = leftOffset1;
  2130. *rightCount = rightCount1;
  2131. *rightOffset = rightOffset1;
  2132. }
  2133. /**************************************************************************\
  2134. *
  2135. * Function Description:
  2136. *
  2137. * Combines left path, right path, start cap, and end cap.
  2138. *
  2139. * Arguments:
  2140. *
  2141. * [OUT] windedPoints - Output point data.
  2142. * [OUT] widnedTypes - Output type data.
  2143. * [IN] isClosed - TRUE is the current suppat is closed.
  2144. * [IN] closeStartCap - TRUE if the start cap needs to be closed.
  2145. * [IN] closeEndCap - TRUE if the end cap needs to be closed.
  2146. *
  2147. * Return Value:
  2148. *
  2149. * Status
  2150. *
  2151. \**************************************************************************/
  2152. GpStatus
  2153. GpPathWidener::CombineSubpathOutlines(
  2154. DynPointFArray* widenedPoints,
  2155. DynByteArray* widenedTypes,
  2156. BOOL isClosed,
  2157. BOOL closeStartCap,
  2158. BOOL closeEndCap
  2159. )
  2160. {
  2161. GpStatus status = Ok;
  2162. INT startCapCount = CapPoints1.GetCount();
  2163. GpPointF* startCapPoints = CapPoints1.GetDataBuffer();
  2164. BYTE* startCapTypes = CapTypes1.GetDataBuffer();
  2165. INT endCapCount = CapPoints2.GetCount();
  2166. GpPointF* endCapPoints = CapPoints2.GetDataBuffer();
  2167. BYTE* endCapTypes = CapTypes2.GetDataBuffer();
  2168. BYTE* leftTypes;
  2169. GpPointF* leftPoints;
  2170. BYTE* rightTypes;
  2171. GpPointF* rightPoints;
  2172. INT leftCount, rightCount;
  2173. leftCount = LeftPoints.GetCount();
  2174. leftTypes = LeftTypes.GetDataBuffer();
  2175. leftPoints = LeftPoints.GetDataBuffer();
  2176. rightCount = RightPoints.GetCount();
  2177. rightTypes = RightTypes.GetDataBuffer();
  2178. rightPoints = RightPoints.GetDataBuffer();
  2179. if(!isClosed)
  2180. {
  2181. GpPointF *grad = Gradients.GetDataBuffer();
  2182. INT gradCount = Gradients.GetCount();
  2183. INT leftOffset, rightOffset;
  2184. modifyEdges(leftPoints, leftTypes, &leftCount, &leftOffset,
  2185. rightPoints, rightTypes, &rightCount, &rightOffset,
  2186. grad, gradCount);
  2187. leftPoints += leftOffset;
  2188. leftTypes += leftOffset;
  2189. rightPoints += rightOffset;
  2190. rightTypes += rightOffset;
  2191. }
  2192. status = widenedPoints->ReserveSpace(
  2193. leftCount + rightCount + startCapCount + endCapCount + 2);
  2194. if(status == Ok)
  2195. status = widenedTypes->ReserveSpace(
  2196. leftCount + rightCount + startCapCount + endCapCount + 2);
  2197. GpPointF* wPts = NULL;
  2198. BYTE* wTypes = NULL;
  2199. if(status == Ok)
  2200. {
  2201. wPts = widenedPoints->GetDataBuffer();
  2202. wTypes = widenedTypes->GetDataBuffer();
  2203. }
  2204. if(wPts && wTypes)
  2205. {
  2206. // Set the pointers to the current location.
  2207. INT count0 = widenedPoints->GetCount();
  2208. wPts += count0;
  2209. wTypes += count0;
  2210. INT resultCount;
  2211. BOOL isStartCapClosed = FALSE;
  2212. BOOL isEndCapClosed = FALSE;
  2213. if(isClosed)
  2214. {
  2215. leftTypes[leftCount - 1] |= PathPointTypeCloseSubpath;
  2216. rightTypes[rightCount - 1] |= PathPointTypeCloseSubpath;
  2217. }
  2218. else
  2219. {
  2220. if(startCapCount > 0)
  2221. {
  2222. if(!closeStartCap)
  2223. {
  2224. if(startCapTypes[startCapCount - 1] & PathPointTypeCloseSubpath)
  2225. isStartCapClosed = TRUE;
  2226. }
  2227. else
  2228. {
  2229. // Force the start cap to be closed.
  2230. startCapTypes[startCapCount - 1] |= PathPointTypeCloseSubpath;
  2231. isStartCapClosed = TRUE;
  2232. }
  2233. }
  2234. if(endCapCount > 0)
  2235. {
  2236. if(!closeEndCap)
  2237. {
  2238. if(endCapTypes[endCapCount - 1] & PathPointTypeCloseSubpath)
  2239. isEndCapClosed = TRUE;
  2240. }
  2241. else
  2242. {
  2243. // Force the end cap to be closed.
  2244. endCapTypes[endCapCount - 1] |= PathPointTypeCloseSubpath;
  2245. isEndCapClosed = TRUE;
  2246. }
  2247. }
  2248. }
  2249. if(isClosed || (startCapCount == 0 && endCapCount == 0))
  2250. {
  2251. BOOL connect = TRUE;
  2252. resultCount =
  2253. ::CombinePaths(leftCount + rightCount, wPts, wTypes,
  2254. leftCount, leftPoints, leftTypes, TRUE,
  2255. rightCount, rightPoints, rightTypes, FALSE,
  2256. connect);
  2257. }
  2258. else
  2259. {
  2260. resultCount = leftCount;
  2261. if(leftCount > 0)
  2262. {
  2263. GpMemcpy(wPts, leftPoints, leftCount*sizeof(GpPointF));
  2264. GpMemcpy(wTypes, leftTypes, leftCount);
  2265. }
  2266. if(endCapCount > 0 && !isEndCapClosed)
  2267. {
  2268. resultCount =
  2269. combineTwoOpenSegments(
  2270. resultCount, wPts, wTypes, TRUE,
  2271. endCapCount, endCapPoints, endCapTypes, TRUE);
  2272. }
  2273. if(rightCount > 0)
  2274. {
  2275. resultCount =
  2276. combineTwoOpenSegments(
  2277. resultCount, wPts, wTypes, TRUE,
  2278. rightCount, rightPoints, rightTypes, FALSE);
  2279. }
  2280. if(startCapCount > 0 && !isStartCapClosed)
  2281. {
  2282. resultCount =
  2283. combineTwoOpenSegments(
  2284. resultCount, wPts, wTypes, TRUE,
  2285. startCapCount, startCapPoints, startCapTypes, TRUE);
  2286. }
  2287. wTypes[0] = PathPointTypeStart;
  2288. }
  2289. if(resultCount > 0)
  2290. {
  2291. // If the original subpath is open, the combined path needs to be
  2292. // closed. If the original path is closed, the left and
  2293. // right paths are already closed.
  2294. if(!isClosed)
  2295. {
  2296. wTypes[resultCount - 1] |= PathPointTypeCloseSubpath;
  2297. // Add the closed caps.
  2298. if(endCapCount > 0 && isEndCapClosed)
  2299. {
  2300. resultCount =
  2301. combineClosedSegments(
  2302. resultCount, wPts, wTypes, TRUE,
  2303. endCapCount, endCapPoints, endCapTypes, TRUE);
  2304. }
  2305. if(startCapCount > 0 && isStartCapClosed)
  2306. {
  2307. resultCount =
  2308. combineClosedSegments(
  2309. resultCount, wPts, wTypes, TRUE,
  2310. startCapCount, startCapPoints, startCapTypes, TRUE);
  2311. }
  2312. }
  2313. widenedPoints->AdjustCount(resultCount);
  2314. widenedTypes->AdjustCount(resultCount);
  2315. }
  2316. else
  2317. status = GenericError;
  2318. }
  2319. else
  2320. status = OutOfMemory;
  2321. return status;
  2322. }
  2323. /**************************************************************************\
  2324. *
  2325. * Function Description:
  2326. *
  2327. * Combines the closed cap paths.
  2328. *
  2329. * Arguments:
  2330. *
  2331. * [OUT] windedPoints - Output point data.
  2332. * [OUT] widnedTypes - Output type data.
  2333. *
  2334. * Return Value:
  2335. *
  2336. * Status
  2337. *
  2338. \**************************************************************************/
  2339. GpStatus
  2340. GpPathWidener::CombineClosedCaps(
  2341. DynPointFArray* widenedPoints,
  2342. DynByteArray* widenedTypes,
  2343. DynPointFArray *daStartCapPoints,
  2344. DynPointFArray *daEndCapPoints,
  2345. DynByteArray *daStartCapTypes,
  2346. DynByteArray *daEndCapTypes
  2347. )
  2348. {
  2349. GpStatus status = Ok;
  2350. INT startCapCount = daStartCapPoints->GetCount();
  2351. GpPointF* startCapPoints = daStartCapPoints->GetDataBuffer();
  2352. BYTE* startCapTypes = daStartCapTypes->GetDataBuffer();
  2353. INT endCapCount = daEndCapPoints->GetCount();
  2354. GpPointF* endCapPoints = daEndCapPoints->GetDataBuffer();
  2355. BYTE* endCapTypes = daEndCapTypes->GetDataBuffer();
  2356. if(startCapCount == 0 && endCapCount == 0)
  2357. {
  2358. return status;
  2359. }
  2360. status = widenedPoints->ReserveSpace(startCapCount + endCapCount);
  2361. if(status == Ok)
  2362. status = widenedTypes->ReserveSpace(startCapCount + endCapCount);
  2363. GpPointF* wPts = NULL;
  2364. BYTE* wTypes = NULL;
  2365. if(status == Ok)
  2366. {
  2367. wPts = widenedPoints->GetDataBuffer();
  2368. wTypes = widenedTypes->GetDataBuffer();
  2369. }
  2370. else
  2371. status = OutOfMemory;
  2372. if(status == Ok && wPts && wTypes)
  2373. {
  2374. INT count0 = widenedPoints->GetCount();
  2375. // Make sure the previous path is closed.
  2376. if(count0 > 0)
  2377. wTypes[count0 - 1] |= PathPointTypeCloseSubpath;
  2378. // Set the pointers to the current location.
  2379. wPts += count0;
  2380. wTypes += count0;
  2381. INT resultCount = 0;
  2382. if(startCapCount > 0)
  2383. {
  2384. // Force the start cap to be closed.
  2385. startCapTypes[startCapCount - 1] |= PathPointTypeCloseSubpath;
  2386. resultCount =
  2387. combineClosedSegments(
  2388. resultCount, wPts, wTypes, TRUE,
  2389. startCapCount, startCapPoints, startCapTypes, TRUE);
  2390. }
  2391. if(endCapCount > 0)
  2392. {
  2393. // Force the end cap to be closed.
  2394. endCapTypes[endCapCount - 1] |= PathPointTypeCloseSubpath;
  2395. resultCount =
  2396. combineClosedSegments(
  2397. resultCount, wPts, wTypes, TRUE,
  2398. endCapCount, endCapPoints, endCapTypes, TRUE);
  2399. }
  2400. widenedPoints->AdjustCount(resultCount);
  2401. widenedTypes->AdjustCount(resultCount);
  2402. }
  2403. return status;
  2404. }
  2405. GpTurningDirection
  2406. getTurningDirection(
  2407. REAL* crossProduct,
  2408. const GpPointF& grad1,
  2409. const GpPointF& grad2
  2410. )
  2411. {
  2412. ASSERT(crossProduct);
  2413. GpTurningDirection direction = NotTurning;
  2414. *crossProduct = 0;
  2415. // Handle the degenerate cases.
  2416. GpPointF v;
  2417. if(( (REALABS(grad1.X) < REAL_EPSILON) &&
  2418. (REALABS(grad1.Y) < REAL_EPSILON) ) ||
  2419. ( (REALABS(grad2.X) < REAL_EPSILON) &&
  2420. (REALABS(grad2.Y) < REAL_EPSILON) )
  2421. )
  2422. {
  2423. return NotTurning;
  2424. }
  2425. // Handle the case of straight or nearly straight lines.
  2426. // The following constant is completely bogus - we need a number here
  2427. // and we're fairly certain it must be small. Probably a better estimate
  2428. // would be some fraction of a device pixel over the length of the line -
  2429. // if we can figure out how big that is.
  2430. const REAL gradErr = 0.00001f;
  2431. if(distance_squared(grad1, grad2) < gradErr)
  2432. {
  2433. direction = NotTurning;
  2434. return direction;
  2435. }
  2436. // Calculate the cross product.
  2437. REAL cross = grad1.X*grad2.Y - grad1.Y*grad2.X;
  2438. // When it comes here, the lines are turning.
  2439. // Get the turning direction.
  2440. if (REALABS(cross) <= REAL_EPSILON)
  2441. {
  2442. direction = TurningBack;
  2443. cross = 0;
  2444. }
  2445. else
  2446. {
  2447. if(cross > 0)
  2448. {
  2449. direction = TurningRight;
  2450. }
  2451. else // if(cross < 0)
  2452. {
  2453. direction = TurningLeft;
  2454. }
  2455. }
  2456. *crossProduct = cross;
  2457. return direction;
  2458. }
  2459. /**************************************************************************\
  2460. *
  2461. * Function Description:
  2462. *
  2463. * Calculates if the miter join will exceed the miter limit.
  2464. *
  2465. * Arguments:
  2466. *
  2467. * [IN] grad1 - the unit tangent vector of the last edge.
  2468. * [IN] grad2 - the unit tangent vector of the current edge.
  2469. * [IN] miterLimit2 - the square of the Miter limit.
  2470. *
  2471. * Return Value:
  2472. *
  2473. * TRUE if the miter limit of this join is exceeded
  2474. *
  2475. \**************************************************************************/
  2476. BOOL
  2477. getMiterExceeded(
  2478. const GpPointF& grad1,
  2479. const GpPointF& grad2,
  2480. REAL miterLimit2
  2481. )
  2482. {
  2483. REAL cross = grad1.X*grad2.Y - grad1.Y*grad2.X;
  2484. // If cross product is zero, the lines are colinear and can be
  2485. // turning back on themselves.
  2486. if (REALABS(cross) <= REAL_EPSILON)
  2487. {
  2488. return TRUE;
  2489. }
  2490. // Get the normal direction for the miter join.
  2491. GpPointF v(0, 0);
  2492. v.X = grad1.X - grad2.X;
  2493. v.Y = grad1.Y - grad2.Y;
  2494. // Test the miter limit.
  2495. REAL test = v.X*v.X + v.Y*v.Y - cross*cross*miterLimit2;
  2496. return test > 0;
  2497. }
  2498. /**************************************************************************\
  2499. *
  2500. * Function Description:
  2501. *
  2502. * Calculates the vector for Miter or Bevel join. This vector represents
  2503. * the shift to the left along the moving direction.
  2504. * In case of Miter join, when the Miter join exceeds the miter limit,
  2505. * this returns the Bevel join.
  2506. *
  2507. * Arguments:
  2508. *
  2509. * [OUT] vector - the left shift for the Miter join. This must be
  2510. * allocated at least for the dimension of 2.
  2511. * [OUT] count - the number of join points.
  2512. * [IN] miterLimit2 - the square of the Miter limit.
  2513. * [IN] grad1 - the unit tangent vector of the last edge.
  2514. * [IN] grad2 - the unit tangent vector of the current edge.
  2515. *
  2516. * Return Value:
  2517. *
  2518. * Turning direction from the last edge to the current edge.
  2519. *
  2520. \**************************************************************************/
  2521. GpTurningDirection
  2522. getMiterBevelJoin(
  2523. const GpPointF& point,
  2524. const GpPointF& grad1,
  2525. const GpPointF& grad2,
  2526. const GpPointF& norm1,
  2527. const GpPointF& norm2,
  2528. REAL leftWidth,
  2529. REAL rightWidth,
  2530. INT *leftCount,
  2531. GpPointF *leftPoints,
  2532. BOOL* leftInside,
  2533. INT *rightCount,
  2534. GpPointF *rightPoints,
  2535. BOOL* rightInside,
  2536. BOOL needsToAdjustNormals,
  2537. REAL miterLimit2,
  2538. BOOL isMiter,
  2539. BOOL useBevelJoinInside
  2540. )
  2541. {
  2542. *leftInside = FALSE;
  2543. *rightInside = FALSE;
  2544. if(miterLimit2 <= 1)
  2545. isMiter = FALSE;
  2546. GpTurningDirection direction = NotTurning;
  2547. // Handle the degenerate cases.
  2548. GpPointF v(0, 0);
  2549. REAL cross;
  2550. direction = getTurningDirection(&cross, grad1, grad2);
  2551. if(direction == NotMoving)
  2552. {
  2553. *leftCount = 0;
  2554. *rightCount = 0;
  2555. return direction;
  2556. }
  2557. else if(direction == NotTurning)
  2558. {
  2559. if(norm1.X != 0 || norm1.Y != 0)
  2560. v = norm1;
  2561. else
  2562. v = norm2;
  2563. leftPoints[0].X = point.X + leftWidth*v.X;
  2564. leftPoints[0].Y = point.Y + leftWidth*v.Y;
  2565. *leftCount = 1;
  2566. rightPoints[0].X = point.X + rightWidth*v.X;
  2567. rightPoints[0].Y = point.Y + rightWidth*v.Y;
  2568. *rightCount = 1;
  2569. return direction;
  2570. }
  2571. if(cross > 0)
  2572. {
  2573. // Right Turn
  2574. // If the width is positive, this point is outside.
  2575. // For the zero width, we regard this as non-inside point.
  2576. if(leftWidth >= 0)
  2577. *leftInside = FALSE;
  2578. else
  2579. *leftInside = TRUE;
  2580. if(rightWidth >= 0)
  2581. *rightInside = FALSE;
  2582. else
  2583. *rightInside = TRUE;
  2584. }
  2585. else
  2586. {
  2587. // Left Turn
  2588. // If the width is negative, this point is outside.
  2589. // For the zero width, we regard this as non-inside point.
  2590. if(leftWidth <= 0)
  2591. *leftInside = FALSE;
  2592. else
  2593. *leftInside = TRUE;
  2594. if(rightWidth <= 0)
  2595. *rightInside = FALSE;
  2596. else
  2597. *rightInside = TRUE;
  2598. }
  2599. BOOL isLeftMiterJoin = FALSE, isRightMiterJoin = FALSE;
  2600. REAL leftShift1 = 0, rightShift1 = 0;
  2601. REAL leftShift2 = 0, rightShift2 = 0;
  2602. if(isMiter && cross != 0)
  2603. {
  2604. REAL test = 0;
  2605. // Get the normal direction for the miter join.
  2606. v.X = grad1.X - grad2.X;
  2607. v.Y = grad1.Y - grad2.Y;
  2608. // Test the miter limit.
  2609. test = v.X*v.X + v.Y*v.Y - cross*cross*miterLimit2;
  2610. if(test <= 0 )
  2611. {
  2612. // Use the miter join.
  2613. if(needsToAdjustNormals)
  2614. {
  2615. // Use adjusted normals so that aliased thin lines
  2616. // won't disappear.
  2617. REAL c1, c2;
  2618. c1 = norm2.X*grad2.Y - norm2.Y*grad2.X;
  2619. c2 = norm1.X*grad1.Y - norm1.Y*grad1.X;
  2620. v.X = c1*grad1.X - c2*grad2.X;
  2621. v.Y = c1*grad1.Y - c2*grad2.Y;
  2622. }
  2623. v.X /= cross;
  2624. v.Y /= cross;
  2625. GpPointF *outPoints, *inPoints;
  2626. REAL outWidth, inWidth;
  2627. INT *outCount, *inCount;
  2628. if(cross > 0)
  2629. {
  2630. // When a miter join is used, set the inside flag to
  2631. // FALSE since there is no overlap.
  2632. isLeftMiterJoin = TRUE;
  2633. *leftInside = FALSE;
  2634. if(useBevelJoinInside)
  2635. {
  2636. if(*rightInside)
  2637. isRightMiterJoin = FALSE;
  2638. else
  2639. {
  2640. // When the right edges are outside,
  2641. // we cannot use Bevel join since
  2642. // Bevel join shape will actually appear.
  2643. isRightMiterJoin = TRUE;
  2644. }
  2645. }
  2646. else
  2647. {
  2648. // When a miter join is used, set the inside flag to
  2649. // FALSE since there is no overlap.
  2650. isRightMiterJoin = TRUE;
  2651. *rightInside = FALSE;
  2652. }
  2653. }
  2654. else
  2655. {
  2656. // When a miter join is used, set the inside flag to
  2657. // FALSE since there is no overlap.
  2658. isRightMiterJoin = TRUE;
  2659. *rightInside = FALSE;
  2660. if(useBevelJoinInside)
  2661. {
  2662. if(*leftInside)
  2663. isLeftMiterJoin = FALSE;
  2664. else
  2665. {
  2666. // When the right edges are outside,
  2667. // we cannot use Bevel join since
  2668. // Bevel join shape will actually appear.
  2669. isLeftMiterJoin = TRUE;
  2670. }
  2671. }
  2672. else
  2673. {
  2674. // When a miter join is used, set the inside flag to
  2675. // FALSE since there is no overlap.
  2676. isLeftMiterJoin = TRUE;
  2677. *leftInside = FALSE;
  2678. }
  2679. }
  2680. }
  2681. else
  2682. {
  2683. // The turn is too sharp and it exceeds the miter limit.
  2684. // We must chop off the miter join tips.
  2685. REAL n1n1 = 1, n2n2 = 1, g1n1 = 0, g2n2 = 0;
  2686. if(needsToAdjustNormals)
  2687. {
  2688. n1n1 = norm1.X*norm1.X + norm1.Y*norm1.Y;
  2689. n2n2 = norm2.X*norm2.X + norm2.Y*norm2.Y;
  2690. g1n1 = grad1.X*norm1.X + grad1.Y*norm1.Y;
  2691. g2n2 = grad2.X*norm2.X + grad2.Y*norm2.Y;
  2692. }
  2693. if(miterLimit2 > max(n1n1, n2n2))
  2694. {
  2695. if(*leftInside == FALSE)
  2696. {
  2697. REAL lWidth;
  2698. if(cross > 0)
  2699. lWidth = leftWidth; // Right Turn
  2700. else
  2701. lWidth = - leftWidth; // Left Turn
  2702. leftShift1 = (REALSQRT(miterLimit2 - n1n1 + g1n1*g1n1)
  2703. - g1n1)*lWidth;
  2704. leftShift2 = (REALSQRT(miterLimit2 - n2n2 + g2n2*g2n2)
  2705. + g2n2)*lWidth;
  2706. }
  2707. if(*rightInside == FALSE)
  2708. {
  2709. REAL rWidth;
  2710. if(cross > 0)
  2711. rWidth = rightWidth; // Right Turn
  2712. else
  2713. rWidth = - rightWidth; // Left Turn
  2714. rightShift1 = (REALSQRT(miterLimit2 - n1n1 + g1n1*g1n1)
  2715. - g1n1)*rWidth;
  2716. rightShift2 = (REALSQRT(miterLimit2 - n2n2 + g2n2*g2n2)
  2717. + g2n2)*rWidth;
  2718. }
  2719. }
  2720. }
  2721. }
  2722. if(isLeftMiterJoin)
  2723. {
  2724. leftPoints[0].X = point.X + leftWidth*v.X;
  2725. leftPoints[0].Y = point.Y + leftWidth*v.Y;
  2726. *leftCount = 1;
  2727. }
  2728. else
  2729. {
  2730. leftPoints[0].X = point.X + leftWidth*norm1.X + leftShift1*grad1.X;
  2731. leftPoints[0].Y = point.Y + leftWidth*norm1.Y + leftShift1*grad1.Y;
  2732. leftPoints[1].X = point.X + leftWidth*norm2.X - leftShift2*grad2.X;
  2733. leftPoints[1].Y = point.Y + leftWidth*norm2.Y - leftShift2*grad2.Y;
  2734. // Check if two points are degenerate.
  2735. if(REALABS(leftPoints[1].X - leftPoints[0].X) +
  2736. REALABS(leftPoints[1].Y - leftPoints[0].Y)
  2737. > POINTF_EPSILON)
  2738. {
  2739. *leftCount = 2;
  2740. }
  2741. else
  2742. {
  2743. // Since there is no overlap, set the inside flag to FALSE.
  2744. *leftCount = 1;
  2745. *leftInside = FALSE;
  2746. }
  2747. }
  2748. if(isRightMiterJoin)
  2749. {
  2750. rightPoints[0].X = point.X + rightWidth*v.X;
  2751. rightPoints[0].Y = point.Y + rightWidth*v.Y;
  2752. *rightCount = 1;
  2753. }
  2754. else
  2755. {
  2756. rightPoints[0].X = point.X + rightWidth*norm1.X + rightShift1*grad1.X;
  2757. rightPoints[0].Y = point.Y + rightWidth*norm1.Y + rightShift1*grad1.Y;
  2758. rightPoints[1].X = point.X + rightWidth*norm2.X - rightShift2*grad2.X;
  2759. rightPoints[1].Y = point.Y + rightWidth*norm2.Y - rightShift2*grad2.Y;
  2760. // Check if two points are degenerate.
  2761. if(REALABS(rightPoints[1].X - rightPoints[0].X) +
  2762. REALABS(rightPoints[1].Y - rightPoints[0].Y)
  2763. > POINTF_EPSILON)
  2764. {
  2765. *rightCount = 2;
  2766. }
  2767. else
  2768. {
  2769. // Since there is no overlap, set the inside flag to FALSE.
  2770. *rightCount = 1;
  2771. *rightInside = FALSE;
  2772. }
  2773. }
  2774. return direction;
  2775. }
  2776. enum GpRoundJoinFlag
  2777. {
  2778. NeedsNone = 0,
  2779. NeedsOnlyRoundJoin = 1,
  2780. NeedsOnlyNonRoundJoin = 2,
  2781. NeedsBoth = 3
  2782. };
  2783. /**************************************************************************\
  2784. *
  2785. * Function Description:
  2786. *
  2787. * From the given point and the two tangent vectors of the two edges,
  2788. * and the radius of the round join, this returns the verteces for
  2789. * left edges and right edges of the round join for the current point.
  2790. * This is used when the bending angle is less than 90 degrees
  2791. * and is called by GetRoundJoin.
  2792. *
  2793. * Arguments:
  2794. *
  2795. * [IN] point - The current points in the original path.
  2796. * [IN] grad1 - The tangent of the current edge.
  2797. * [IN] grad2 - The tangent of the next edge.
  2798. * [IN] dot - The dot product of grad1 and grad2.
  2799. * [IN] leftWidth - The left width of the round join.
  2800. * [IN] rightWidth - The right width of the round join.
  2801. * [OUT] leftCount - The count of the left points.
  2802. * [OUT] leftPoints - The left points.
  2803. * [OUT] rightCount - The count of the right points.
  2804. * [OUT] rightPoints - The right points.
  2805. *
  2806. * Both leftPoints and rightPoints must have at least dimension of 4.
  2807. * If leftCount is positive (negative), this means the left edges are
  2808. * lines with leftCount points (cubic Bezier curves with -leftCount
  2809. * control points).
  2810. * If rightCount is positive (negative), this means the right edges are
  2811. * lines with rightCount points (cubic Bezier curves with -rightCount
  2812. * control points).
  2813. *
  2814. * Return Value:
  2815. *
  2816. * None
  2817. *
  2818. * 06/16/99 ikkof
  2819. * Created it
  2820. *
  2821. \**************************************************************************/
  2822. VOID
  2823. getSmallRoundJoin(
  2824. const GpPointF& point,
  2825. const GpPointF& grad1,
  2826. const GpPointF& grad2,
  2827. const GpPointF& norm1,
  2828. const GpPointF& norm2,
  2829. REAL leftWidth,
  2830. REAL rightWidth,
  2831. INT *leftCount,
  2832. GpPointF *leftPoints,
  2833. INT *rightCount,
  2834. GpPointF *rightPoints,
  2835. REAL dot,
  2836. REAL cross,
  2837. BOOL needsToAdjustNormals,
  2838. REAL miterLimit2,
  2839. INT condition,
  2840. BOOL useBevelJoinInside
  2841. )
  2842. {
  2843. if((condition & NeedsBoth) == 0)
  2844. {
  2845. *leftCount = 0;
  2846. *rightCount = 0;
  2847. return;
  2848. }
  2849. GpPointF n1, n2;
  2850. n1 = norm1;
  2851. n2 = norm2;
  2852. REAL k;
  2853. REAL almostStraight = 1.0f - 0.01f;
  2854. if(dot < almostStraight)
  2855. {
  2856. // Obtain the distance from the first control point
  2857. // or from the last control point.
  2858. // For its derivation, see ikkof's notes for "Round Joins".
  2859. REAL cross1 = cross;
  2860. if(cross < 0)
  2861. cross1 = - cross;
  2862. k = 4*(REALSQRT(2*(1 - dot)) - cross1)/(3*(1 - dot));
  2863. GpPointF *outPoints, *inPoints;
  2864. INT *outCount, *inCount;
  2865. REAL outWidth, inWidth;
  2866. if(cross >= 0)
  2867. {
  2868. // The left edges are round join.
  2869. outPoints = leftPoints;
  2870. inPoints = rightPoints;
  2871. outCount = leftCount;
  2872. inCount = rightCount;
  2873. outWidth = leftWidth;
  2874. inWidth = rightWidth;
  2875. }
  2876. else
  2877. {
  2878. // The right edges are round join.
  2879. outPoints = rightPoints;
  2880. inPoints = leftPoints;
  2881. outCount = rightCount;
  2882. inCount = leftCount;
  2883. outWidth = - rightWidth;
  2884. inWidth = - leftWidth;
  2885. n1.X = - n1.X;
  2886. n1.Y = - n1.Y;
  2887. n2.X = - n2.X;
  2888. n2.Y = - n2.Y;
  2889. }
  2890. // Get the normal direction for the miter join.
  2891. GpPointF v;
  2892. v.X = grad1.X - grad2.X;
  2893. v.Y = grad1.Y - grad2.Y;
  2894. // Test the miter limit.
  2895. BOOL useMiterJoin = FALSE;;
  2896. // Reduce the miter limit
  2897. miterLimit2 = 3*3;
  2898. // Note that abs(cross) == abs(cross1) from the definition.
  2899. if(REALABS(cross1) >= REAL_EPSILON)
  2900. {
  2901. REAL test = v.X*v.X + v.Y*v.Y - cross*cross*miterLimit2;
  2902. if(test <= 0)
  2903. {
  2904. useMiterJoin = TRUE;
  2905. v.X /= cross1;
  2906. v.Y /= cross1;
  2907. }
  2908. }
  2909. useMiterJoin = useMiterJoin && !useBevelJoinInside;
  2910. REAL k1;
  2911. if(outWidth > 0)
  2912. {
  2913. if(condition & NeedsOnlyRoundJoin)
  2914. {
  2915. k1 = outWidth*k;
  2916. outPoints[0].X = point.X + outWidth*n1.X;
  2917. outPoints[0].Y = point.Y + outWidth*n1.Y;
  2918. outPoints[1].X = outPoints[0].X + k1*grad1.X;
  2919. outPoints[1].Y = outPoints[0].Y + k1*grad1.Y;
  2920. outPoints[3].X = point.X + outWidth*n2.X;
  2921. outPoints[3].Y = point.Y + outWidth*n2.Y;
  2922. outPoints[2].X = outPoints[3].X - k1*grad2.X;
  2923. outPoints[2].Y = outPoints[3].Y - k1*grad2.Y;
  2924. *outCount = -4; // Indicate "-" for Bezier
  2925. }
  2926. else
  2927. *outCount = 0;
  2928. }
  2929. else
  2930. {
  2931. if(condition & NeedsOnlyNonRoundJoin)
  2932. {
  2933. if(outWidth == 0)
  2934. {
  2935. outPoints[0] = point;
  2936. *outCount = 1;
  2937. }
  2938. else
  2939. {
  2940. if(useMiterJoin)
  2941. {
  2942. outPoints[0].X = point.X + outWidth*v.X;
  2943. outPoints[0].Y = point.Y + outWidth*v.Y;
  2944. *outCount = 1;
  2945. }
  2946. else
  2947. {
  2948. outPoints[0].X = point.X + outWidth*n1.X;
  2949. outPoints[0].Y = point.Y + outWidth*n1.Y;
  2950. outPoints[1].X = point.X + outWidth*n2.X;
  2951. outPoints[1].Y = point.Y + outWidth*n2.Y;
  2952. *outCount = 2;
  2953. }
  2954. }
  2955. }
  2956. else
  2957. *outCount = 0;
  2958. }
  2959. if(inWidth > 0)
  2960. {
  2961. if(condition & NeedsOnlyRoundJoin)
  2962. {
  2963. k1 = inWidth*k;
  2964. inPoints[0].X = point.X + inWidth*n1.X;
  2965. inPoints[0].Y = point.Y + inWidth*n1.Y;
  2966. inPoints[1].X = inPoints[0].X + k1*grad1.X;
  2967. inPoints[1].Y = inPoints[0].Y + k1*grad1.Y;
  2968. inPoints[3].X = point.X + inWidth*n2.X;
  2969. inPoints[3].Y = point.Y + inWidth*n2.Y;
  2970. inPoints[2].X = inPoints[3].X - k1*grad2.X;
  2971. inPoints[2].Y = inPoints[3].Y - k1*grad2.Y;
  2972. *inCount = -4; // Indicate "-" for Bezier
  2973. }
  2974. else
  2975. *inCount = 0;
  2976. }
  2977. else
  2978. {
  2979. if(condition & NeedsOnlyNonRoundJoin)
  2980. {
  2981. if(inWidth == 0)
  2982. {
  2983. inPoints[0] = point;
  2984. *inCount = 1;
  2985. }
  2986. else
  2987. {
  2988. if(useMiterJoin)
  2989. {
  2990. inPoints[0].X = point.X + inWidth*v.X;
  2991. inPoints[0].Y = point.Y + inWidth*v.Y;
  2992. *inCount = 1;
  2993. }
  2994. else
  2995. {
  2996. inPoints[0].X = point.X + inWidth*n1.X;
  2997. inPoints[0].Y = point.Y + inWidth*n1.Y;
  2998. inPoints[1].X = point.X + inWidth*n2.X;
  2999. inPoints[1].Y = point.Y + inWidth*n2.Y;
  3000. *inCount = 2;
  3001. }
  3002. }
  3003. }
  3004. else
  3005. *inCount = 0;
  3006. }
  3007. }
  3008. else
  3009. {
  3010. if(condition & NeedsOnlyNonRoundJoin)
  3011. {
  3012. // This is a straight line.
  3013. leftPoints[0].X = point.X + leftWidth*n1.X;
  3014. leftPoints[0].Y = point.Y + leftWidth*n1.Y;
  3015. *leftCount = 1;
  3016. rightPoints[0].X = point.X + rightWidth*n1.X;
  3017. rightPoints[0].Y = point.Y + rightWidth*n1.Y;
  3018. *rightCount = 1;
  3019. }
  3020. else
  3021. {
  3022. *leftCount = 0;
  3023. *rightCount = 0;
  3024. }
  3025. }
  3026. }
  3027. /**************************************************************************\
  3028. *
  3029. * Function Description:
  3030. *
  3031. * From the given previous, current, and next points and the radius
  3032. * of the round join, this returns the verteces for left edges and
  3033. * right edges of the round join for the current point.
  3034. *
  3035. * Arguments:
  3036. *
  3037. * [IN] points - The previous, current, and next points
  3038. * in the original path.
  3039. * [IN] leftWidth - The left width of the round join.
  3040. * [IN] rightWidth - The right width of the round join.
  3041. * [OUT] leftCount - The count of the left points.
  3042. * [OUT] leftPoints - The left points.
  3043. * [OUT] rightCount - The count of the right points.
  3044. * [OUT] rightPoints - The right points.
  3045. *
  3046. * Both leftPoints and rightPoints must have at least dimension of 7.
  3047. * If leftCount is positive (negative), this means the left edges are
  3048. * lines with leftCount points (cubic Bezier curves with -leftCount
  3049. * control points).
  3050. * If rightCount is positive (negative), this means the right edges are
  3051. * lines with rightCount points (cubic Bezier curves with -rightCount
  3052. * control points).
  3053. *
  3054. * Return Value:
  3055. *
  3056. * FALSE if the current point coindes with the previous point or
  3057. * the next point. Otherwise, this returns TRUE.
  3058. *
  3059. * 06/16/99 ikkof
  3060. * Created it
  3061. *
  3062. \**************************************************************************/
  3063. GpTurningDirection
  3064. getRoundJoin(
  3065. const GpPointF& point,
  3066. const GpPointF& grad1,
  3067. const GpPointF& grad2,
  3068. const GpPointF& norm1,
  3069. const GpPointF& norm2,
  3070. REAL leftWidth,
  3071. REAL rightWidth,
  3072. INT* leftCount,
  3073. GpPointF* leftPoints,
  3074. BOOL* leftInside,
  3075. INT* rightCount,
  3076. GpPointF* rightPoints,
  3077. BOOL* rightInside,
  3078. BOOL needsToAdjustNormals,
  3079. REAL miterLimit2,
  3080. BOOL useBevelJoinInside
  3081. )
  3082. {
  3083. //!!! We need to update inside flags for Round joins later.
  3084. *leftInside = FALSE;
  3085. *rightInside = FALSE;
  3086. ASSERT(leftPoints && rightPoints);
  3087. ASSERT(leftCount && rightCount);
  3088. REAL radius = leftWidth;
  3089. // When it comes here, the three points are not degenerate.
  3090. REAL dot = grad1.X*grad2.X + grad1.Y*grad2.Y; // dot product.
  3091. REAL cross;
  3092. GpTurningDirection direction = getTurningDirection(
  3093. &cross, grad1, grad2);
  3094. // &cross, grad1, grad2, norm1, norm2);
  3095. // If dot >= 0 (the bending angle is less than or equal to 90 degrees,
  3096. // we can approximate this arc with one cubic Beizer curve.
  3097. INT condition;
  3098. REAL smallErr = - 0.001f;
  3099. if(dot > smallErr)
  3100. {
  3101. condition = NeedsBoth;
  3102. getSmallRoundJoin(point, grad1, grad2, norm1, norm2,
  3103. leftWidth, rightWidth,
  3104. leftCount, leftPoints, rightCount, rightPoints,
  3105. dot, cross, needsToAdjustNormals, miterLimit2,
  3106. condition, useBevelJoinInside);
  3107. }
  3108. else
  3109. {
  3110. // The bending angle is larger than 90 and less than or
  3111. // equal to 180 degrees.
  3112. // We can approximate this arc with two cubic Beizer curves.
  3113. GpPointF *pts1, *pts2;
  3114. INT count1, count2;
  3115. pts1 = leftPoints;
  3116. pts2 = rightPoints;
  3117. // First obtain the non-round join parts.
  3118. condition = NeedsOnlyNonRoundJoin;
  3119. getSmallRoundJoin(point, grad1, grad2, norm1, norm2,
  3120. leftWidth, rightWidth,
  3121. &count1, pts1, &count2, pts2,
  3122. dot, cross, needsToAdjustNormals, miterLimit2,
  3123. condition, useBevelJoinInside);
  3124. INT cnt1, cnt2;
  3125. if(count1 > 0)
  3126. cnt1 = count1;
  3127. else
  3128. cnt1 = 0;
  3129. if(count2 > 0)
  3130. cnt2 = count2;
  3131. else
  3132. cnt2 = 0;
  3133. pts1 += cnt1;
  3134. pts2 += cnt2;
  3135. *leftCount = cnt1;
  3136. *rightCount = cnt2;
  3137. // Obtain the middle unit gradient vector.
  3138. GpPointF midNorm;
  3139. midNorm.X = norm1.X + norm2.X;
  3140. midNorm.Y = norm1.Y + norm2.Y;
  3141. if(midNorm.X != 0 || midNorm.Y != 0)
  3142. {
  3143. REAL dm = midNorm.X*midNorm.X + midNorm.Y*midNorm.Y;
  3144. dm = REALSQRT(dm);
  3145. midNorm.X /= dm;
  3146. midNorm.Y /= dm;
  3147. }
  3148. else
  3149. {
  3150. midNorm.X = - norm1.Y;
  3151. midNorm.Y = norm1.X;
  3152. }
  3153. GpPointF lm;
  3154. // Rotate the mid normal +90 degrees.
  3155. lm.X = - midNorm.Y;
  3156. lm.Y = midNorm.X;
  3157. // Obtain the first half of the round join.
  3158. condition = NeedsOnlyRoundJoin;
  3159. dot = grad1.X*lm.X + grad1.Y*lm.Y;
  3160. cross = grad1.X*lm.Y - grad1.Y*lm.X;
  3161. getSmallRoundJoin(point, grad1, lm, norm1, midNorm,
  3162. leftWidth, rightWidth,
  3163. &count1, pts1, &count2, pts2,
  3164. dot, cross, needsToAdjustNormals, miterLimit2,
  3165. condition, useBevelJoinInside);
  3166. // Note that since the end point of the first half of
  3167. // the round join and the start point of the second
  3168. // of the round join are the same, don't copy
  3169. // the end point of the first half of the round join.
  3170. if(count1 < 0)
  3171. cnt1 = - count1 - 1;
  3172. else
  3173. cnt1 = 0;
  3174. if(count2 < 0)
  3175. cnt2 = - count2 - 1;
  3176. else
  3177. cnt2 = 0;
  3178. pts1 += cnt1;
  3179. pts2 += cnt2;
  3180. *leftCount += cnt1;
  3181. *rightCount += cnt2;
  3182. // Obtain the second half of the round join.
  3183. dot = lm.X*grad2.X + lm.Y*grad2.Y;
  3184. cross = lm.X*grad2.Y - lm.Y*grad2.X;
  3185. getSmallRoundJoin(point, lm, grad2, midNorm, norm2,
  3186. leftWidth, rightWidth,
  3187. &count1, pts1, &count2, pts2,
  3188. dot, cross, needsToAdjustNormals, miterLimit2,
  3189. condition, useBevelJoinInside);
  3190. // Combines the two curves or lines.
  3191. if(count1 < 0)
  3192. cnt1 += - count1;
  3193. else
  3194. cnt1 = 0;
  3195. if(count2 < 0)
  3196. cnt2 += - count2;
  3197. else
  3198. cnt2 = 0;
  3199. if(cnt1 > 0)
  3200. *leftCount = - cnt1;
  3201. if(cnt2 > 0)
  3202. *rightCount = - cnt2;
  3203. }
  3204. return direction;
  3205. }
  3206. /**************************************************************************\
  3207. *
  3208. * Function Description:
  3209. *
  3210. * Calculates the vector for Miter or Bevel join. This vector represents
  3211. * the shift to the left along the moving direction.
  3212. * In case of Miter join, when the Miter join exceeds the miter limit,
  3213. * this returns the Bevel join.
  3214. *
  3215. * Arguments:
  3216. *
  3217. * [OUT] vector - the left shift for the Miter join. This must be
  3218. * allocated at least for the dimension of 2.
  3219. * [OUT] count - the number of join points.
  3220. * [IN] miterLimit2 - the square of the Miter limit.
  3221. * [IN] grad1 - the unit tangent vector of the last edge.
  3222. * [IN] grad2 - the unit tangent vector of the current edge.
  3223. *
  3224. * Return Value:
  3225. *
  3226. * Turning direction from the last edge to the current edge.
  3227. *
  3228. \**************************************************************************/
  3229. GpTurningDirection
  3230. getHobbyJoin(
  3231. const GpPointF& point,
  3232. const GpPointF& grad1,
  3233. const GpPointF& grad2,
  3234. INT polyCount,
  3235. const GpPointF* polyPoints,
  3236. const REAL* polyAngles,
  3237. // const GpPointF& norm1,
  3238. // const GpPointF& norm2,
  3239. REAL leftWidth,
  3240. REAL rightWidth,
  3241. INT *leftCount,
  3242. GpPointF *leftPoints,
  3243. INT *rightCount,
  3244. GpPointF *rightPoints,
  3245. BOOL needsToAdjustNormals,
  3246. REAL miterLimit2,
  3247. BOOL isMiter,
  3248. BOOL useBevelJoinInside
  3249. )
  3250. {
  3251. if(miterLimit2 <= 1)
  3252. isMiter = FALSE;
  3253. GpTurningDirection direction = NotTurning;
  3254. // Handle the degenerate cases.
  3255. GpPointF v;
  3256. REAL cross;
  3257. direction = getTurningDirection(&cross, grad1, grad2);
  3258. if(direction == NotMoving)
  3259. {
  3260. *leftCount = 0;
  3261. *rightCount = 0;
  3262. return direction;
  3263. }
  3264. // Find the left vertex ids.
  3265. INT leftIndex1, leftIndex2;
  3266. leftIndex1 = getVertexID(grad1, TRUE, polyCount, polyAngles);
  3267. leftIndex2 = getVertexID(grad2, TRUE, polyCount, polyAngles);
  3268. INT i;
  3269. if(direction == TurningLeft)
  3270. {
  3271. *leftCount = 2;
  3272. leftPoints[0] = point + polyPoints[leftIndex1];
  3273. leftPoints[1] = point + polyPoints[leftIndex2];
  3274. }
  3275. else if(direction == TurningRight)
  3276. {
  3277. if(leftIndex2 > leftIndex1)
  3278. {
  3279. *leftCount = leftIndex2 - leftIndex1 + 1;
  3280. for(i = 0; i <= leftIndex2 - leftIndex1; i++)
  3281. leftPoints[i] = point + polyPoints[i + leftIndex1];
  3282. }
  3283. else if(leftIndex2 < leftIndex1)
  3284. {
  3285. *leftCount = polyCount - leftIndex1 + leftIndex2 + 1;
  3286. for(i = 0; i < polyCount - leftIndex1; i++)
  3287. leftPoints[i] = point + polyPoints[i + leftIndex1];
  3288. for(i = 0; i <= leftIndex2; i++)
  3289. leftPoints[polyCount - leftIndex1 + i]
  3290. = point + polyPoints[i];
  3291. }
  3292. else
  3293. {
  3294. *leftCount = 1;
  3295. leftPoints[0] = point + polyPoints[leftIndex1];
  3296. }
  3297. }
  3298. else
  3299. {
  3300. *leftCount = 1;
  3301. leftPoints[0] = point + polyPoints[leftIndex1];
  3302. }
  3303. INT rightIndex1, rightIndex2;
  3304. rightIndex1 = getVertexID(grad1, FALSE, polyCount, polyAngles);
  3305. rightIndex2 = getVertexID(grad2, FALSE, polyCount, polyAngles);
  3306. if(direction == TurningRight)
  3307. {
  3308. *rightCount = 2;
  3309. rightPoints[0] = point + polyPoints[rightIndex1];
  3310. rightPoints[1] = point + polyPoints[rightIndex2];
  3311. }
  3312. else if(direction == TurningLeft)
  3313. {
  3314. if(rightIndex1 > rightIndex2)
  3315. {
  3316. *rightCount = rightIndex1 - rightIndex2 + 1;
  3317. for(i = 0; i <= rightIndex1 - rightIndex2; i++)
  3318. rightPoints[i] = point + polyPoints[rightIndex1 - i];
  3319. }
  3320. else if(rightIndex1 < rightIndex2)
  3321. {
  3322. *rightCount = polyCount - rightIndex2 + rightIndex1 + 1;
  3323. for(i = 0; i <= rightIndex1; i++)
  3324. rightPoints[i] = point + polyPoints[rightIndex1 - i];
  3325. for(i = 0; i < polyCount - rightIndex2; i++)
  3326. rightPoints[rightIndex1 + 1 + i]
  3327. = point + polyPoints[polyCount - i - 1];
  3328. }
  3329. else
  3330. {
  3331. *rightCount = 1;
  3332. rightPoints[0] = point + polyPoints[rightIndex1];
  3333. }
  3334. }
  3335. else
  3336. {
  3337. *rightCount = 1;
  3338. rightPoints[0] = point + polyPoints[rightIndex1];
  3339. }
  3340. return direction;
  3341. }
  3342. GpTurningDirection
  3343. getJoin(
  3344. GpLineJoin lineJoin,
  3345. const GpPointF& point,
  3346. const GpPointF& grad1,
  3347. const GpPointF& grad2,
  3348. const GpPointF& norm1,
  3349. const GpPointF& norm2,
  3350. REAL leftWidth,
  3351. REAL rightWidth,
  3352. INT *leftCount,
  3353. GpPointF *leftPoints,
  3354. BOOL *leftInside,
  3355. INT *rightCount,
  3356. GpPointF *rightPoints,
  3357. BOOL *rightInside,
  3358. BOOL needsToAdjustNormals,
  3359. REAL miterLimit2,
  3360. BOOL useBevelJoinInside
  3361. )
  3362. {
  3363. BOOL isMiter = TRUE;
  3364. GpTurningDirection direction;
  3365. switch(lineJoin)
  3366. {
  3367. case LineJoinBevel:
  3368. isMiter = FALSE; // Fall through to Miter case.
  3369. case LineJoinMiterClipped:
  3370. // Treat Miter clipped joints that exceed the miter limit as
  3371. // beveled joints. Fall through to Miter case.
  3372. if (lineJoin == LineJoinMiterClipped &&
  3373. getMiterExceeded(grad1, grad2, miterLimit2))
  3374. {
  3375. isMiter = FALSE;
  3376. }
  3377. case LineJoinMiter:
  3378. direction = getMiterBevelJoin(point, grad1, grad2, norm1, norm2,
  3379. leftWidth, rightWidth,
  3380. leftCount, leftPoints, leftInside,
  3381. rightCount, rightPoints, rightInside,
  3382. needsToAdjustNormals, miterLimit2, isMiter, useBevelJoinInside);
  3383. break;
  3384. case LineJoinRound:
  3385. direction = getRoundJoin(point, grad1, grad2, norm1, norm2,
  3386. leftWidth, rightWidth,
  3387. leftCount, leftPoints, leftInside,
  3388. rightCount, rightPoints, rightInside,
  3389. needsToAdjustNormals, miterLimit2, useBevelJoinInside);
  3390. break;
  3391. }
  3392. return direction;
  3393. }
  3394. /**************************************************************************\
  3395. *
  3396. * Function Description:
  3397. *
  3398. * From the given the reference point, gradient, and widths,
  3399. * this returns the verteces for the round cap.
  3400. * The direction of the round cap is always clockwise.
  3401. *
  3402. * Arguments:
  3403. *
  3404. * [IN] point - The reference point.
  3405. * [IN] grad - The gradient.
  3406. * [IN] isStartCap - TRUE if this is the start cap.
  3407. * [IN] leftWidth - The left width from the reference.
  3408. * [IN] rightWidth - The right width from the reference point.
  3409. *
  3410. *
  3411. * Return Value:
  3412. *
  3413. * Ok if successfull.
  3414. *
  3415. * 06/16/99 ikkof
  3416. * Created it
  3417. *
  3418. \**************************************************************************/
  3419. GpStatus
  3420. GpPathWidener::SetRoundCap(
  3421. const GpPointF& point,
  3422. const GpPointF& grad,
  3423. BOOL isStartCap,
  3424. REAL leftWidth,
  3425. REAL rightWidth
  3426. )
  3427. {
  3428. if( (REALABS(grad.X) < REAL_EPSILON) &&
  3429. (REALABS(grad.Y) < REAL_EPSILON) )
  3430. {
  3431. return InvalidParameter;
  3432. }
  3433. GpPointF* capPoints = NULL;
  3434. BYTE* capTypes = NULL;
  3435. if(isStartCap)
  3436. {
  3437. CapPoints1.Reset(FALSE);
  3438. CapTypes1.Reset(FALSE);
  3439. capPoints = CapPoints1.AddMultiple(7);
  3440. if(capPoints)
  3441. capTypes = CapTypes1.AddMultiple(7);
  3442. if(!capPoints || !capTypes)
  3443. return OutOfMemory;
  3444. }
  3445. else
  3446. {
  3447. CapPoints2.Reset(FALSE);
  3448. CapTypes2.Reset(FALSE);
  3449. capPoints = CapPoints2.AddMultiple(7);
  3450. if(capPoints)
  3451. capTypes = CapTypes2.AddMultiple(7);
  3452. if(!capPoints || !capTypes)
  3453. return OutOfMemory;
  3454. }
  3455. GpMemset(capTypes, PathPointTypeBezier, 7);
  3456. capTypes[0] = PathPointTypeLine;
  3457. GpPointF tangent;
  3458. if(isStartCap)
  3459. {
  3460. tangent.X = - grad.X;
  3461. tangent.Y = - grad.Y;
  3462. }
  3463. else
  3464. tangent = grad;
  3465. REAL radius = (leftWidth - rightWidth)/2;
  3466. GpPointF center;
  3467. center.X = point.X + (leftWidth + rightWidth)*grad.Y/2;
  3468. center.Y = point.Y - (leftWidth + rightWidth)*grad.X/2;
  3469. if(isStartCap)
  3470. {
  3471. center.X -= Inset1*tangent.X;
  3472. center.Y -= Inset1*tangent.Y;
  3473. }
  3474. else
  3475. {
  3476. center.X -= Inset2*tangent.X;
  3477. center.Y -= Inset2*tangent.Y;
  3478. }
  3479. REAL s1, c1;
  3480. // Direction of the left normal multipled by radius.
  3481. c1 = radius*tangent.Y;
  3482. s1 = - radius*tangent.X;
  3483. // 2 Bezier segments for a half circle with radius 1.
  3484. REAL u_cir = U_CIR;
  3485. capPoints[ 0].X = 1; capPoints[ 0].Y = 0;
  3486. capPoints[ 1].X = 1; capPoints[ 1].Y = u_cir;
  3487. capPoints[ 2].X = u_cir; capPoints[ 2].Y = 1;
  3488. capPoints[ 3].X = 0; capPoints[ 3].Y = 1;
  3489. capPoints[ 4].X = -u_cir; capPoints[ 4].Y = 1;
  3490. capPoints[ 5].X = -1; capPoints[ 5].Y = u_cir;
  3491. capPoints[ 6].X = -1; capPoints[ 6].Y = 0;
  3492. // Rotate, scale, and translate the original half circle.
  3493. for(INT i = 0; i < 7; i++)
  3494. {
  3495. REAL x, y;
  3496. x = capPoints[i].X;
  3497. y = capPoints[i].Y;
  3498. capPoints[i].X = (c1*x - s1*y) + center.X;
  3499. capPoints[i].Y = (s1*x + c1*y) + center.Y;
  3500. }
  3501. return Ok;
  3502. }
  3503. /**************************************************************************\
  3504. *
  3505. * Function Description:
  3506. *
  3507. * Creates a double round cap for inset pen ('B' shaped)
  3508. *
  3509. * Arguments:
  3510. *
  3511. * [IN] point - The reference point.
  3512. * [IN] grad - The gradient.
  3513. * [IN] isStartCap - TRUE if this is the start cap.
  3514. * [IN] leftWidth - The left width from the reference.
  3515. * [IN] rightWidth - The right width from the reference point.
  3516. *
  3517. *
  3518. * Return Value:
  3519. *
  3520. * Ok if successfull.
  3521. *
  3522. * 10/01/2000 asecchia
  3523. * Created it
  3524. *
  3525. \**************************************************************************/
  3526. GpStatus
  3527. GpPathWidener::SetDoubleRoundCap(
  3528. const GpPointF& point,
  3529. const GpPointF& grad,
  3530. BOOL isStartCap,
  3531. REAL leftWidth,
  3532. REAL rightWidth
  3533. )
  3534. {
  3535. if( (REALABS(grad.X) < REAL_EPSILON) &&
  3536. (REALABS(grad.Y) < REAL_EPSILON) )
  3537. {
  3538. return InvalidParameter;
  3539. }
  3540. GpPointF* capPoints = NULL;
  3541. BYTE* capTypes = NULL;
  3542. if(isStartCap)
  3543. {
  3544. CapPoints1.Reset(FALSE);
  3545. CapTypes1.Reset(FALSE);
  3546. capPoints = CapPoints1.AddMultiple(14);
  3547. if(capPoints)
  3548. capTypes = CapTypes1.AddMultiple(14);
  3549. if(!capPoints || !capTypes)
  3550. return OutOfMemory;
  3551. }
  3552. else
  3553. {
  3554. CapPoints2.Reset(FALSE);
  3555. CapTypes2.Reset(FALSE);
  3556. capPoints = CapPoints2.AddMultiple(14);
  3557. if(capPoints)
  3558. capTypes = CapTypes2.AddMultiple(14);
  3559. if(!capPoints || !capTypes)
  3560. return OutOfMemory;
  3561. }
  3562. GpMemset(capTypes, PathPointTypeBezier, 14);
  3563. capTypes[0] = PathPointTypeLine;
  3564. capTypes[7] = PathPointTypeLine;
  3565. GpPointF tangent;
  3566. if(isStartCap)
  3567. {
  3568. tangent.X = - grad.X;
  3569. tangent.Y = - grad.Y;
  3570. }
  3571. else
  3572. tangent = grad;
  3573. REAL radius = (leftWidth - rightWidth)/2;
  3574. GpPointF center;
  3575. center.X = point.X + (leftWidth + rightWidth)*grad.Y/2;
  3576. center.Y = point.Y - (leftWidth + rightWidth)*grad.X/2;
  3577. if(isStartCap)
  3578. {
  3579. center.X -= Inset1*tangent.X;
  3580. center.Y -= Inset1*tangent.Y;
  3581. }
  3582. else
  3583. {
  3584. center.X -= Inset2*tangent.X;
  3585. center.Y -= Inset2*tangent.Y;
  3586. }
  3587. REAL s1, c1;
  3588. // Direction of the left normal multipled by radius.
  3589. c1 = radius*tangent.Y;
  3590. s1 = - radius*tangent.X;
  3591. // 2 Bezier segments for a half circle with radius 1.
  3592. REAL u_cir = U_CIR;
  3593. capPoints[ 0].X = 1; capPoints[ 0].Y = 0;
  3594. capPoints[ 1].X = 1; capPoints[ 1].Y = u_cir;
  3595. capPoints[ 2].X = u_cir; capPoints[ 2].Y = 1;
  3596. capPoints[ 3].X = 0; capPoints[ 3].Y = 1;
  3597. capPoints[ 4].X = -u_cir; capPoints[ 4].Y = 1;
  3598. capPoints[ 5].X = -1; capPoints[ 5].Y = u_cir;
  3599. capPoints[ 6].X = -1; capPoints[ 6].Y = 0;
  3600. // Create the second bump and scale the first one.
  3601. for(int i=0; i<7; i++)
  3602. {
  3603. capPoints[i+7].X = capPoints[i].X * 0.5f-0.5f;
  3604. capPoints[i+7].Y = capPoints[i].Y * 0.5f;
  3605. capPoints[i].X = 0.5f + capPoints[i].X * 0.5f;
  3606. capPoints[i].Y = capPoints[i].Y * 0.5f;
  3607. }
  3608. // Rotate, scale, and translate the original half circle.
  3609. for(INT i = 0; i < 14; i++)
  3610. {
  3611. REAL x, y;
  3612. x = capPoints[i].X;
  3613. y = capPoints[i].Y;
  3614. capPoints[i].X = (c1*x - s1*y) + center.X;
  3615. capPoints[i].Y = (s1*x + c1*y) + center.Y;
  3616. }
  3617. return Ok;
  3618. }
  3619. GpStatus
  3620. GpPathWidener::SetTriangleCap(
  3621. const GpPointF& point,
  3622. const GpPointF& grad,
  3623. BOOL isStartCap,
  3624. REAL leftWidth,
  3625. REAL rightWidth,
  3626. const GpPointF *points,
  3627. INT pointCount
  3628. )
  3629. {
  3630. if( (REALABS(grad.X) < REAL_EPSILON) &&
  3631. (REALABS(grad.Y) < REAL_EPSILON) )
  3632. {
  3633. return InvalidParameter;
  3634. }
  3635. GpPointF* capPoints = NULL;
  3636. BYTE* capTypes = NULL;
  3637. DynByteArray *capTypesArray;
  3638. DynPointFArray *capPointsArray;
  3639. if(isStartCap)
  3640. {
  3641. CapPoints1.Reset(FALSE);
  3642. CapTypes1.Reset(FALSE);
  3643. capPoints = CapPoints1.AddMultiple(3);
  3644. if(capPoints)
  3645. capTypes = CapTypes1.AddMultiple(3);
  3646. if(!capPoints || !capTypes)
  3647. return OutOfMemory;
  3648. }
  3649. else
  3650. {
  3651. CapPoints2.Reset(FALSE);
  3652. CapTypes2.Reset(FALSE);
  3653. capPoints = CapPoints2.AddMultiple(3);
  3654. if(capPoints)
  3655. capTypes = CapTypes2.AddMultiple(3);
  3656. if(!capPoints || !capTypes)
  3657. return OutOfMemory;
  3658. }
  3659. GpMemset(&capTypes[0], PathPointTypeLine, 3);
  3660. GpPointF norm, tangent;
  3661. norm.X = grad.Y;
  3662. norm.Y = - grad.X;
  3663. if(isStartCap)
  3664. {
  3665. tangent.X = - grad.X;
  3666. tangent.Y = - grad.Y;
  3667. }
  3668. else
  3669. {
  3670. tangent = grad;
  3671. }
  3672. GpPointF leftPt, rightPt;
  3673. leftPt.X = point.X + leftWidth*norm.X;
  3674. leftPt.Y = point.Y + leftWidth*norm.Y;
  3675. rightPt.X = point.X + rightWidth*norm.X;
  3676. rightPt.Y = point.Y + rightWidth*norm.Y;
  3677. GpPointF center;
  3678. REAL width = REALABS(leftWidth-rightWidth);
  3679. center.X = 0.5f*(leftPt.X + rightPt.X + width*tangent.X);
  3680. center.Y = 0.5f*(leftPt.Y + rightPt.Y + width*tangent.Y);
  3681. capPoints[1] = center;
  3682. if(isStartCap)
  3683. {
  3684. capPoints[0] = rightPt;
  3685. capPoints[2] = leftPt;
  3686. }
  3687. else
  3688. {
  3689. capPoints[0] = leftPt;
  3690. capPoints[2] = rightPt;
  3691. }
  3692. return Ok;
  3693. }
  3694. /**************************************************************************\
  3695. *
  3696. * Function Description:
  3697. *
  3698. * Add the first widened point of the current path type.
  3699. *
  3700. * Arguments:
  3701. *
  3702. * [IN] leftWidth - The left width for widened line.
  3703. * [IN] rightWidth - The right width for the widened line.
  3704. * [IN] lineJoin - The type of the line join.
  3705. * [OUT] leftPoints1 - The buffer for the left points.
  3706. * [OUT] leftTypes1 - The buffer for the left types.
  3707. * [OUT] addedLeftCount - The number of the added left points and types.
  3708. * [OUT] rightPoints1 - The buffer for the right points.
  3709. * [OUT] rightTypes1 - The buffer for the right types.
  3710. * [OUT] addedRightCount - The number of the added right points and types.
  3711. * [OUT] leftEndPt - The end point of the left line for the current
  3712. * subpath. This is calculated only for the first
  3713. * subpath point.
  3714. * [OUT] rightEndPt - The end point of the right line for tha current
  3715. * subpath. This is calculated only for the first
  3716. * subpath point.
  3717. * [IN] grad - The gradients of the center points for the
  3718. * current path type.
  3719. * [IN] dataPoints - The center points data for the current path type
  3720. * [IN] dataCount - The number of data points in the current path type.
  3721. * [IN/OUT] lastPt - The last point used in calculations.
  3722. * [IN] flag - The flag to indicates the various properties
  3723. * of the current subpath and type.
  3724. *
  3725. *
  3726. * Return Value:
  3727. *
  3728. * NONE
  3729. *
  3730. * 01/24/2000 ikkof
  3731. * Created it
  3732. *
  3733. \**************************************************************************/
  3734. VOID
  3735. GpPathWidener::WidenFirstPoint(
  3736. REAL leftWidth,
  3737. REAL rightWidth,
  3738. GpLineJoin lineJoin,
  3739. REAL miterLimit2,
  3740. GpPointF* leftPoints,
  3741. BYTE* leftTypes,
  3742. INT* addedLeftCount,
  3743. GpPointF* rightPoints,
  3744. BYTE* rightTypes,
  3745. INT* addedRightCount,
  3746. GpPointF* leftEndPt,
  3747. GpPointF* rightEndPt,
  3748. const GpPointF* grad,
  3749. const GpPointF* norm,
  3750. const GpPointF* dataPoints,
  3751. INT dataCount,
  3752. GpPointF* lastPt,
  3753. const REAL* firstInsets,
  3754. INT flag
  3755. )
  3756. {
  3757. GpPointF nextPt = dataPoints[0];
  3758. GpPointF grad1, grad2;
  3759. GpPointF norm1, norm2;
  3760. INT leftCount = 0;
  3761. INT rightCount = 0;
  3762. grad1 = *grad++;
  3763. grad2 = *grad;
  3764. norm1 = *norm++;
  3765. norm2 = *norm;
  3766. INT numOfAddedFirstPts = 0;
  3767. if(flag & WideningFirstType)
  3768. {
  3769. BOOL needsToAdjustNormals = FALSE;
  3770. GpLineJoin lineJoin1 = lineJoin;
  3771. if(flag & WideningNeedsToAdjustNormals)
  3772. {
  3773. needsToAdjustNormals = TRUE;
  3774. lineJoin1 = LineJoinMiter; // Don't use RoundJoin.
  3775. }
  3776. if(!(flag & WideningClosed))
  3777. {
  3778. lineJoin1 = LineJoinBevel;
  3779. }
  3780. const INT bufferCount = 32;
  3781. GpPointF lPts[bufferCount], rPts[bufferCount];
  3782. INT lCnt, rCnt;
  3783. GpTurningDirection direction;
  3784. BOOL useBevelJoinInside = (flag & WideningUseBevelJoinInside) != 0;
  3785. INT polyCount = JoinPolygonPoints.GetCount();
  3786. const GpPointF* polyPoints = JoinPolygonPoints.GetDataBuffer();
  3787. const REAL* polyAngles = JoinPolygonAngles.GetDataBuffer();
  3788. BOOL leftInside = FALSE, rightInside = FALSE;
  3789. if(polyCount > 0)
  3790. direction = getHobbyJoin(
  3791. // lineJoin1,
  3792. nextPt,
  3793. grad1,
  3794. grad2,
  3795. polyCount,
  3796. polyPoints,
  3797. polyAngles,
  3798. leftWidth,
  3799. rightWidth,
  3800. &lCnt,
  3801. &lPts[0],
  3802. &rCnt,
  3803. &rPts[0],
  3804. needsToAdjustNormals,
  3805. miterLimit2,
  3806. FALSE, // IsMiter
  3807. useBevelJoinInside
  3808. );
  3809. else
  3810. direction = getJoin(
  3811. lineJoin1,
  3812. nextPt,
  3813. grad1,
  3814. grad2,
  3815. norm1,
  3816. norm2,
  3817. leftWidth,
  3818. rightWidth,
  3819. &lCnt,
  3820. &lPts[0],
  3821. &leftInside,
  3822. &rCnt,
  3823. &rPts[0],
  3824. &rightInside,
  3825. needsToAdjustNormals,
  3826. miterLimit2,
  3827. useBevelJoinInside
  3828. );
  3829. //!!! Inside flag check
  3830. if(leftInside)
  3831. {
  3832. ASSERT((lCnt & 0x01) == 0);
  3833. }
  3834. //!!! Inside flag check
  3835. if(rightInside)
  3836. {
  3837. ASSERT((rCnt & 0x01) == 0);
  3838. }
  3839. *leftEndPt = lPts[0];
  3840. *rightEndPt = rPts[0];
  3841. BYTE pathType;
  3842. if(flag & WideningClosed)
  3843. {
  3844. if(lCnt > 0)
  3845. {
  3846. pathType = PathPointTypeLine;
  3847. }
  3848. else if(lCnt < 0)
  3849. {
  3850. lCnt = - lCnt;
  3851. pathType = PathPointTypeBezier;
  3852. }
  3853. if(lCnt > 0)
  3854. {
  3855. //!!! Inside flag check
  3856. if(leftInside)
  3857. {
  3858. ASSERT((lCnt & 0x01) == 0);
  3859. }
  3860. if(leftInside)
  3861. pathType |= PathPointTypeInternalUse;
  3862. GpMemset(leftTypes, pathType, lCnt);
  3863. leftTypes[0] = PathPointTypeStart;
  3864. if(leftInside)
  3865. leftTypes[0] |= PathPointTypeInternalUse;
  3866. GpMemcpy(leftPoints, &lPts[0], lCnt*sizeof(GpPointF));
  3867. leftTypes += lCnt;
  3868. leftPoints += lCnt;
  3869. leftCount += lCnt;
  3870. }
  3871. if(rCnt > 0)
  3872. {
  3873. pathType = PathPointTypeLine;
  3874. }
  3875. else if(rCnt < 0)
  3876. {
  3877. rCnt = - rCnt;
  3878. pathType = PathPointTypeBezier;
  3879. }
  3880. if(rCnt > 0)
  3881. {
  3882. //!!! Inside flag check
  3883. if(rightInside)
  3884. {
  3885. ASSERT((rCnt & 0x01) == 0);
  3886. }
  3887. if(rightInside)
  3888. pathType |= PathPointTypeInternalUse;
  3889. GpMemset(rightTypes, pathType, rCnt);
  3890. rightTypes[0] = PathPointTypeStart;
  3891. if(rightInside)
  3892. rightTypes[0] |= PathPointTypeInternalUse;
  3893. GpMemcpy(rightPoints, &rPts[0], rCnt*sizeof(GpPointF));
  3894. rightTypes += rCnt;
  3895. rightPoints += rCnt;
  3896. rightCount += rCnt;
  3897. }
  3898. }
  3899. else
  3900. {
  3901. // The path is not closed. Bevel join is used.
  3902. GpPointF leftStartPt;
  3903. GpPointF rightStartPt;
  3904. INT index;
  3905. if(lCnt == 1)
  3906. index = 0;
  3907. else
  3908. index = 1;
  3909. leftStartPt = lPts[index];
  3910. if(rCnt == 1)
  3911. index = 0;
  3912. else
  3913. index = 1;
  3914. rightStartPt = rPts[index];
  3915. if(!(flag & WideningClosed) && firstInsets[0] != 0)
  3916. {
  3917. leftStartPt.X += firstInsets[0]*grad2.X;
  3918. leftStartPt.Y += firstInsets[0]*grad2.Y;
  3919. }
  3920. if(!(flag & WideningClosed) && firstInsets[1] != 0)
  3921. {
  3922. rightStartPt.X += firstInsets[1]*grad2.X;
  3923. rightStartPt.Y += firstInsets[1]*grad2.Y;
  3924. }
  3925. *leftTypes++ = PathPointTypeStart;
  3926. *rightTypes++ = PathPointTypeStart;
  3927. *leftPoints = leftStartPt;
  3928. *rightPoints = rightStartPt;
  3929. leftPoints++;
  3930. rightPoints++;
  3931. leftCount++;
  3932. rightCount++;
  3933. }
  3934. *lastPt = nextPt;
  3935. }
  3936. else
  3937. {
  3938. leftCount = rightCount = 0;
  3939. }
  3940. *addedLeftCount = leftCount;
  3941. *addedRightCount = rightCount;
  3942. }
  3943. /**************************************************************************\
  3944. *
  3945. * Function Description:
  3946. *
  3947. * Add the widened points for Lines
  3948. *
  3949. * For the arguments, See comments for widenFirstPoints
  3950. *
  3951. \**************************************************************************/
  3952. GpStatus
  3953. GpPathWidener::WidenLinePoints(
  3954. REAL leftWidth,
  3955. REAL rightWidth,
  3956. GpLineJoin lineJoin,
  3957. REAL miterLimit2,
  3958. GpPointF* leftPoints,
  3959. BYTE* leftTypes,
  3960. INT* addedLeftCount,
  3961. GpPointF* rightPoints,
  3962. BYTE* rightTypes,
  3963. INT* addedRightCount,
  3964. const GpPointF* grad,
  3965. const GpPointF* norm,
  3966. const GpPointF* dataPoints,
  3967. INT dataCount,
  3968. GpPointF* lastPt,
  3969. const REAL* lastInsets,
  3970. INT flag
  3971. )
  3972. {
  3973. GpPointF grad1, grad2;
  3974. GpPointF norm1, norm2;
  3975. // Skip the first point since it is already added either by
  3976. // widenFirstPoint() or by the widen call of the previous type.
  3977. dataPoints++;
  3978. dataCount--; // The number of the remaining points.
  3979. // Also skip the first gradient.
  3980. grad++;
  3981. grad1 = *grad++;
  3982. norm++;
  3983. norm1 = *norm++;
  3984. BOOL isLastType = FALSE;
  3985. if(flag & WideningLastType)
  3986. isLastType = TRUE;
  3987. BOOL needsToAdjustNormals = FALSE;
  3988. GpLineJoin lineJoin1 = lineJoin;
  3989. if(flag & WideningNeedsToAdjustNormals)
  3990. {
  3991. needsToAdjustNormals = TRUE;
  3992. lineJoin1 = LineJoinMiter; // Don't use RoundJoin.
  3993. }
  3994. INT leftCount = 0, rightCount = 0;
  3995. BOOL isLastPoint = FALSE;
  3996. BYTE pathType = PathPointTypeLine;
  3997. INT jmax = dataCount;
  3998. if(isLastType)
  3999. {
  4000. if(flag & WideningClosed)
  4001. {
  4002. if(!(flag & WideningLastPointSame))
  4003. {
  4004. // When the subpath is closed, and the last point is not
  4005. // the same as the start point, don't regard this as
  4006. // the last type. Add points as usual.
  4007. isLastType = FALSE;
  4008. }
  4009. else
  4010. {
  4011. // No need to add the last point since this is already
  4012. // added by the first point.
  4013. jmax--;
  4014. }
  4015. }
  4016. }
  4017. BOOL useBevelJoinInside = (flag & WideningUseBevelJoinInside) != 0;
  4018. INT polyCount = JoinPolygonPoints.GetCount();
  4019. const GpPointF* polyPoints = JoinPolygonPoints.GetDataBuffer();
  4020. const REAL* polyAngles = JoinPolygonAngles.GetDataBuffer();
  4021. INT i, j;
  4022. for(j = 0; j < jmax; j++)
  4023. {
  4024. GpPointF nextPt = *dataPoints;
  4025. if(isLastType && (j == dataCount - 1))
  4026. {
  4027. isLastPoint = TRUE;
  4028. lineJoin1 = LineJoinBevel;
  4029. }
  4030. if(lastPt->X != nextPt.X || lastPt->Y != nextPt.Y)
  4031. {
  4032. grad2 = *grad;
  4033. norm2 = *norm;
  4034. const INT bufferCount = 32;
  4035. GpPointF lPts[bufferCount], rPts[bufferCount];
  4036. INT lCnt, rCnt;
  4037. GpTurningDirection direction;
  4038. BOOL leftInside = FALSE, rightInside = FALSE;
  4039. if(polyCount > 0)
  4040. direction = getHobbyJoin(
  4041. // lineJoin1,
  4042. nextPt,
  4043. grad1,
  4044. grad2,
  4045. polyCount,
  4046. polyPoints,
  4047. polyAngles,
  4048. leftWidth,
  4049. rightWidth,
  4050. &lCnt,
  4051. &lPts[0],
  4052. &rCnt,
  4053. &rPts[0],
  4054. needsToAdjustNormals,
  4055. miterLimit2,
  4056. FALSE, // IsMiter
  4057. useBevelJoinInside
  4058. );
  4059. else
  4060. direction = getJoin(
  4061. lineJoin1,
  4062. nextPt,
  4063. grad1,
  4064. grad2,
  4065. norm1,
  4066. norm2,
  4067. leftWidth,
  4068. rightWidth,
  4069. &lCnt,
  4070. &lPts[0],
  4071. &leftInside,
  4072. &rCnt,
  4073. &rPts[0],
  4074. &rightInside,
  4075. needsToAdjustNormals,
  4076. miterLimit2,
  4077. useBevelJoinInside
  4078. );
  4079. //!!! Inside flag check
  4080. if(leftInside)
  4081. {
  4082. ASSERT((lCnt & 0x01) == 0);
  4083. }
  4084. //!!! Inside flag check
  4085. if(rightInside)
  4086. {
  4087. ASSERT((rCnt & 0x01) == 0);
  4088. }
  4089. if(isLastPoint)
  4090. {
  4091. lCnt = 1;
  4092. rCnt = 1;
  4093. leftInside = FALSE;
  4094. rightInside = FALSE;
  4095. if(lastInsets[0] != 0)
  4096. {
  4097. lPts[0].X -= lastInsets[0]*grad1.X;
  4098. lPts[0].Y -= lastInsets[0]*grad1.Y;
  4099. }
  4100. if(lastInsets[1] != 0)
  4101. {
  4102. rPts[0].X -= lastInsets[1]*grad1.X;
  4103. rPts[0].Y -= lastInsets[1]*grad1.Y;
  4104. }
  4105. }
  4106. if(lCnt > 0)
  4107. {
  4108. pathType = PathPointTypeLine;
  4109. }
  4110. else if(lCnt < 0)
  4111. {
  4112. lCnt = - lCnt;
  4113. pathType = PathPointTypeBezier;
  4114. }
  4115. if(lCnt > 0)
  4116. {
  4117. //!!! Inside flag check
  4118. if(leftInside)
  4119. {
  4120. ASSERT((lCnt & 0x01) == 0);
  4121. }
  4122. if(leftInside)
  4123. pathType |= PathPointTypeInternalUse;
  4124. GpMemset(leftTypes, pathType, lCnt);
  4125. leftTypes[0] = PathPointTypeLine;
  4126. if(leftInside)
  4127. leftTypes[0] |= PathPointTypeInternalUse;
  4128. GpMemcpy(leftPoints, &lPts[0], lCnt*sizeof(GpPointF));
  4129. leftTypes += lCnt;
  4130. leftPoints += lCnt;
  4131. leftCount += lCnt;
  4132. }
  4133. if(rCnt > 0)
  4134. {
  4135. pathType = PathPointTypeLine;
  4136. }
  4137. else if(rCnt < 0)
  4138. {
  4139. rCnt = - rCnt;
  4140. pathType = PathPointTypeBezier;
  4141. }
  4142. if(rCnt > 0)
  4143. {
  4144. //!!! Inside flag check
  4145. if(rightInside)
  4146. {
  4147. ASSERT((rCnt & 0x01) == 0);
  4148. }
  4149. if(rightInside)
  4150. pathType |= PathPointTypeInternalUse;
  4151. GpMemset(rightTypes, pathType, rCnt);
  4152. rightTypes[0] = PathPointTypeLine;
  4153. if(rightInside)
  4154. rightTypes[0] |= PathPointTypeInternalUse;
  4155. GpMemcpy(rightPoints, &rPts[0], rCnt*sizeof(GpPointF));
  4156. rightTypes += rCnt;
  4157. rightPoints += rCnt;
  4158. rightCount += rCnt;
  4159. }
  4160. grad1 = grad2;
  4161. norm1 = norm2;
  4162. *lastPt = nextPt;
  4163. }
  4164. grad++;
  4165. norm++;
  4166. dataPoints++;
  4167. }
  4168. *addedLeftCount = leftCount;
  4169. *addedRightCount = rightCount;
  4170. return Ok;
  4171. }
  4172. /**************************************************************************\
  4173. *
  4174. * Function Description:
  4175. *
  4176. * Add the widened points for Beziers
  4177. *
  4178. * For the arguments, See comments for widenFirstPoints
  4179. *
  4180. \**************************************************************************/
  4181. GpStatus
  4182. GpPathWidener::WidenBezierPoints(
  4183. REAL leftWidth,
  4184. REAL rightWidth,
  4185. GpLineJoin lineJoin,
  4186. REAL miterLimit2,
  4187. GpPointF* leftPoints,
  4188. BYTE* leftTypes,
  4189. INT* addedLeftCount,
  4190. GpPointF* rightPoints,
  4191. BYTE* rightTypes,
  4192. INT* addedRightCount,
  4193. const GpPointF* grad,
  4194. const GpPointF* norm,
  4195. const GpPointF* dataPoints,
  4196. INT dataCount,
  4197. GpPointF* lastPt,
  4198. const REAL* lastInsets,
  4199. INT flag
  4200. )
  4201. {
  4202. //!!! Kink removal has not been considered here yet.
  4203. GpPointF grad1, grad2;
  4204. GpPointF norm1, norm2;
  4205. // Skip the first point since it is already added either by
  4206. // widenFirstPoint() or by the widen call of the previous type.
  4207. dataPoints++;
  4208. dataCount--; // The number of the remaining points.
  4209. // Also skip the first gradient.
  4210. grad++;
  4211. grad1 = *grad++;
  4212. norm++;
  4213. norm1 = *norm++;
  4214. BOOL isLastType = FALSE;
  4215. if(flag & WideningLastType)
  4216. isLastType = TRUE;
  4217. BOOL needsToAdjustNormals = FALSE;
  4218. GpLineJoin lineJoin1 = lineJoin;
  4219. if(flag & WideningNeedsToAdjustNormals)
  4220. {
  4221. needsToAdjustNormals = TRUE;
  4222. lineJoin1 = LineJoinMiter; // Don't use RoundJoin.
  4223. }
  4224. INT remainder = dataCount % 3;
  4225. INT bezierCount = dataCount/3;
  4226. ASSERT(remainder == 0); // dataCount must be multiple of 3.
  4227. INT leftCount = 0, rightCount = 0;
  4228. BOOL isLastPoint = FALSE;
  4229. BYTE pathType = PathPointTypeBezier;
  4230. if(isLastType)
  4231. {
  4232. if((flag & WideningClosed) && !(flag & WideningLastPointSame))
  4233. {
  4234. // When the subpath is closed, and the last point is not
  4235. // the same as the start point, don't regard this as
  4236. // the last type. Add points as usual.
  4237. isLastType = FALSE;
  4238. }
  4239. // When the path is closed and the last point is the same,
  4240. // we must do the special treatment since the last join points
  4241. // were already added as the first join points.
  4242. // So keep isLastType to TRUE for this case.
  4243. }
  4244. BOOL useBevelJoinInside = flag & WideningUseBevelJoinInside;
  4245. INT i, j;
  4246. for(j = 0; j < bezierCount; j++)
  4247. {
  4248. for(INT k = 0; k < 3; k++)
  4249. {
  4250. GpPointF nextPt = *dataPoints;
  4251. if(k < 2)
  4252. {
  4253. // Second and third control point.
  4254. lineJoin1 = LineJoinMiter;
  4255. }
  4256. else
  4257. {
  4258. // The last control point.
  4259. // lineJoin1 = lineJoin;
  4260. lineJoin1 = LineJoinRound;
  4261. }
  4262. if(isLastType
  4263. && (j == bezierCount - 1) && (k == 2))
  4264. {
  4265. isLastPoint = TRUE;
  4266. if(!(flag & WideningClosed))
  4267. {
  4268. // When the subpath is not closed, make the
  4269. // last join as Bevel join.
  4270. lineJoin1 = LineJoinBevel;
  4271. // When the subpath is closed, use the current
  4272. // join.
  4273. }
  4274. else
  4275. {
  4276. lineJoin1 = LineJoinRound;
  4277. }
  4278. }
  4279. grad2 = *grad;
  4280. norm2 = *norm;
  4281. GpPointF lPts[7], rPts[7];
  4282. INT lCnt, rCnt;
  4283. GpTurningDirection direction;
  4284. BOOL leftInside = FALSE, rightInside = FALSE;
  4285. direction = getJoin(
  4286. lineJoin1,
  4287. nextPt,
  4288. grad1,
  4289. grad2,
  4290. norm1,
  4291. norm2,
  4292. leftWidth,
  4293. rightWidth,
  4294. &lCnt,
  4295. &lPts[0],
  4296. &leftInside,
  4297. &rCnt,
  4298. &rPts[0],
  4299. &rightInside,
  4300. needsToAdjustNormals,
  4301. miterLimit2,
  4302. useBevelJoinInside
  4303. );
  4304. if(k < 2)
  4305. {
  4306. // In case that the miter join was not availabe
  4307. // for k < 2, take the average of two vectors.
  4308. if(lCnt == 2)
  4309. {
  4310. lPts[0].X = (lPts[0].X + lPts[1].X)/2;
  4311. lPts[0].Y = (lPts[0].Y + lPts[1].Y)/2;
  4312. }
  4313. lCnt = 1;
  4314. if(rCnt == 2)
  4315. {
  4316. rPts[0].X = (rPts[0].X + rPts[1].X)/2;
  4317. rPts[0].Y = (rPts[0].Y + rPts[1].Y)/2;
  4318. }
  4319. rCnt = 1;
  4320. }
  4321. if(isLastPoint)
  4322. {
  4323. // In order to keep the 3n point format for the Bezier
  4324. // curves, we must add the first point of the join
  4325. // points as the last point of the last Bezier segment.
  4326. if(!(flag & WideningClosed))
  4327. {
  4328. lCnt = 1;
  4329. rCnt = 1;
  4330. if(lastInsets[0] != 0)
  4331. {
  4332. lPts[0].X -= lastInsets[0]*grad1.X;
  4333. lPts[0].Y -= lastInsets[0]*grad1.Y;
  4334. }
  4335. if(lastInsets[1] != 0)
  4336. {
  4337. rPts[0].X -= lastInsets[1]*grad1.X;
  4338. rPts[0].Y -= lastInsets[1]*grad1.Y;
  4339. }
  4340. }
  4341. }
  4342. *leftPoints++ = lPts[0];
  4343. *leftTypes++ = pathType;
  4344. leftCount++;
  4345. *rightPoints++ = rPts[0];
  4346. *rightTypes++ = pathType;
  4347. rightCount++;
  4348. if(k == 2)
  4349. {
  4350. if(lCnt > 1)
  4351. {
  4352. *leftPoints++ = lPts[1];
  4353. *leftTypes++ = PathPointTypeLine;
  4354. leftCount++;
  4355. }
  4356. else if(lCnt < 0)
  4357. {
  4358. lCnt = - lCnt;
  4359. ASSERT(lCnt % 3 == 1);
  4360. GpMemcpy(leftPoints, &lPts[1], (lCnt - 1)*sizeof(GpPointF));
  4361. GpMemset(leftTypes, pathType, lCnt - 1);
  4362. leftPoints += lCnt - 1;
  4363. leftTypes += lCnt - 1;
  4364. leftCount += lCnt - 1;
  4365. }
  4366. if(rCnt > 1)
  4367. {
  4368. *rightPoints++ = rPts[1];
  4369. *rightTypes++ = PathPointTypeLine;
  4370. rightCount++;
  4371. }
  4372. else if(rCnt < 0)
  4373. {
  4374. rCnt = - rCnt;
  4375. ASSERT(rCnt % 3 == 1);
  4376. GpMemcpy(rightPoints, &rPts[1], (rCnt - 1)*sizeof(GpPointF));
  4377. GpMemset(rightTypes, pathType, rCnt - 1);
  4378. rightPoints += rCnt - 1;
  4379. rightTypes += rCnt - 1;
  4380. rightCount += rCnt - 1;
  4381. }
  4382. }
  4383. grad1 = grad2;
  4384. norm1 = norm2;
  4385. *lastPt = nextPt;
  4386. grad++;
  4387. norm++;
  4388. dataPoints++;
  4389. }
  4390. }
  4391. *addedLeftCount = leftCount;
  4392. *addedRightCount = rightCount;
  4393. return Ok;
  4394. }
  4395. /**************************************************************************\
  4396. *
  4397. * Function Description:
  4398. *
  4399. * Add the widened points for each path type.
  4400. *
  4401. * For the arguments, See comments for widenFirstPoints
  4402. *
  4403. \**************************************************************************/
  4404. GpStatus
  4405. GpPathWidener::WidenEachPathType(
  4406. BYTE pathType,
  4407. REAL leftWidth,
  4408. REAL rightWidth,
  4409. GpLineJoin lineJoin,
  4410. REAL miterLimit2,
  4411. GpPointF* leftPoints,
  4412. BYTE* leftTypes,
  4413. INT* addedLeftCount,
  4414. GpPointF* rightPoints,
  4415. BYTE* rightTypes,
  4416. INT* addedRightCount,
  4417. const GpPointF* grad,
  4418. const GpPointF* norm,
  4419. const GpPointF* dataPoints,
  4420. INT dataCount,
  4421. GpPointF* lastPt,
  4422. const REAL* lastInsets,
  4423. INT flag
  4424. )
  4425. {
  4426. GpStatus status = GenericError;
  4427. switch(pathType)
  4428. {
  4429. case PathPointTypeLine:
  4430. status = WidenLinePoints(
  4431. leftWidth,
  4432. rightWidth,
  4433. lineJoin,
  4434. miterLimit2,
  4435. leftPoints,
  4436. leftTypes,
  4437. addedLeftCount,
  4438. rightPoints,
  4439. rightTypes,
  4440. addedRightCount,
  4441. grad,
  4442. norm,
  4443. dataPoints,
  4444. dataCount,
  4445. lastPt,
  4446. lastInsets,
  4447. flag);
  4448. break;
  4449. case PathPointTypeBezier:
  4450. status = WidenBezierPoints(
  4451. leftWidth,
  4452. rightWidth,
  4453. lineJoin,
  4454. miterLimit2,
  4455. leftPoints,
  4456. leftTypes,
  4457. addedLeftCount,
  4458. rightPoints,
  4459. rightTypes,
  4460. addedRightCount,
  4461. grad,
  4462. norm,
  4463. dataPoints,
  4464. dataCount,
  4465. lastPt,
  4466. lastInsets,
  4467. flag);
  4468. break;
  4469. default:
  4470. WARNING(("Trying to widen undefined types."));
  4471. break;
  4472. }
  4473. return status;
  4474. }
  4475. REAL
  4476. getCapDelta(
  4477. const DpPen* pen
  4478. )
  4479. {
  4480. GpLineCap startCap = pen->StartCap;
  4481. GpLineCap endCap = pen->EndCap;
  4482. GpLineCap dashCap = pen->DashCap;
  4483. REAL delta = 0, delta1;
  4484. if(!(startCap & LineCapAnchorMask))
  4485. delta1 = 0.5f;
  4486. else
  4487. delta1 = 3.0f; // We must adjust later.
  4488. if(delta < delta1)
  4489. delta = delta1;
  4490. if(!(endCap & LineCapAnchorMask))
  4491. delta1 = 0.5f;
  4492. else
  4493. delta1 = 3.0f; // We must adjust later.
  4494. if(delta < delta1)
  4495. delta = delta1;
  4496. if(!(dashCap & LineCapAnchorMask))
  4497. delta1 = 0.5f;
  4498. else
  4499. delta1 = 3.0f; // We must adjust later.
  4500. if(delta < delta1)
  4501. delta = delta1;
  4502. //!!! Add cutom line case.
  4503. return 1.0f;
  4504. }
  4505. /**************************************************************************\
  4506. *
  4507. * Function Description:
  4508. *
  4509. * This calculates the extra width due to pen.
  4510. *
  4511. * Arguments:
  4512. *
  4513. * None
  4514. *
  4515. * Return Value:
  4516. *
  4517. * The extra width.
  4518. *
  4519. * 02/29/00 ikkof
  4520. * Created it
  4521. *
  4522. \**************************************************************************/
  4523. REAL
  4524. GpPathWidener::GetPenDelta()
  4525. {
  4526. const GpPointF* centerPoints = CenterPoints.GetDataBuffer();
  4527. const BYTE* centerTypes = CenterTypes.GetDataBuffer();
  4528. INT centerCount = CenterPoints.GetCount();
  4529. INT startIndex, endIndex;
  4530. BOOL isClosed;
  4531. GpStatus status = Ok;
  4532. REAL scale;
  4533. switch(Pen->PenAlignment)
  4534. {
  4535. case PenAlignmentCenter:
  4536. default:
  4537. scale = 0.5f;
  4538. break;
  4539. }
  4540. REAL capDelta = getCapDelta(Pen);
  4541. REAL joinDelta = 1.0f;
  4542. if(Pen->Join == LineJoinMiter ||
  4543. Pen->Join == LineJoinMiterClipped)
  4544. {
  4545. while(Iterator.NextSubpath(&startIndex, &endIndex, &isClosed)
  4546. && status == Ok)
  4547. {
  4548. status = CalculateGradients(startIndex, endIndex);
  4549. if(status == Ok)
  4550. {
  4551. REAL delta = GetSubpathPenMiterDelta(isClosed);
  4552. if(delta > joinDelta)
  4553. joinDelta = delta;
  4554. }
  4555. }
  4556. if(status != Ok)
  4557. {
  4558. // We have to use the possible maximum for miter join.
  4559. // Usually this is an over-estimate since the most path
  4560. // don't have very sharp edges which correspond to miter limit.
  4561. joinDelta = Pen->MiterLimit;
  4562. }
  4563. }
  4564. REAL penDelta = max(joinDelta, capDelta)*scale;
  4565. if(NeedsToTransform)
  4566. {
  4567. // This is already in device unit.
  4568. penDelta *= StrokeWidth;
  4569. }
  4570. else
  4571. {
  4572. // Convert the width to the device unit.
  4573. penDelta *= MaximumWidth;
  4574. }
  4575. if(penDelta < 1)
  4576. penDelta = 1;
  4577. return penDelta;
  4578. }
  4579. /**************************************************************************\
  4580. *
  4581. * Function Description:
  4582. *
  4583. * This calculates the extra within a subpath due to a pen.
  4584. * This is called by GetPenDelta().
  4585. *
  4586. * Arguments:
  4587. *
  4588. * None
  4589. *
  4590. * Return Value:
  4591. *
  4592. * The extra width.
  4593. *
  4594. * 02/29/00 ikkof
  4595. * Created it
  4596. *
  4597. \**************************************************************************/
  4598. REAL
  4599. GpPathWidener::GetSubpathPenMiterDelta(
  4600. BOOL isClosed
  4601. )
  4602. {
  4603. INT count = Gradients.GetCount();
  4604. GpPointF* grad0 = Gradients.GetDataBuffer();
  4605. INT imin, imax;
  4606. if(isClosed)
  4607. {
  4608. imin = 0;
  4609. imax = count - 1;
  4610. }
  4611. else
  4612. {
  4613. imin = 1;
  4614. imax = count - 2;
  4615. }
  4616. GpPointF* grad = grad0 + imin;
  4617. GpPointF prevGrad = *grad++;
  4618. GpPointF nextGrad;
  4619. REAL dot = 0;
  4620. for(INT i = imin; i < imax; i++)
  4621. {
  4622. nextGrad = *grad++;
  4623. REAL dot1 = prevGrad.X*nextGrad.X + prevGrad.Y*nextGrad.Y;
  4624. prevGrad = nextGrad;
  4625. if(dot1 < dot)
  4626. dot = dot1;
  4627. }
  4628. REAL cosHalfTheta = (dot + 1.0f)*0.5f;
  4629. REAL miterDelta = Pen->MiterLimit;
  4630. // If the miterDelta is smaller than the miter limit, calculate it.
  4631. if(cosHalfTheta > 0 && cosHalfTheta*miterDelta*miterDelta > 1)
  4632. {
  4633. cosHalfTheta = REALSQRT(cosHalfTheta);
  4634. miterDelta = 1.0f/cosHalfTheta;
  4635. }
  4636. return miterDelta;
  4637. }