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.

1193 lines
32 KiB

  1. /**************************************************************************
  2. *
  3. * Copyright (c) 2000 Microsoft Corporation
  4. *
  5. * Module Name:
  6. *
  7. * End Cap Creator.
  8. *
  9. * Abstract:
  10. *
  11. * This module defines a class called GpEndCapCreator. This class is
  12. * responsible for constructing a path containing all the custom endcaps
  13. * and anchor endcaps for a given path. These are correctly transformed
  14. * and positioned.
  15. *
  16. * This class is used to create and position all the endcaps for a
  17. * given path and pen. This class is also responsible for trimming
  18. * the original path down so that it fits the end caps properly.
  19. * This class will handle all types of end caps except the base endcaps
  20. * (round, flat and triangle) which may be used as dash caps.
  21. * Caps that are handled are CustomCaps and the 3 Anchor caps (round,
  22. * diamond and arrow). Note that the round anchor cap is distinct from
  23. * the round base cap.
  24. *
  25. * Created:
  26. *
  27. * 10/09/2000 asecchia
  28. * Created it.
  29. *
  30. **************************************************************************/
  31. #include "precomp.hpp"
  32. //-------------------------------------------------------------
  33. // GetMajorAndMinorAxis() is defined in PathWidener.cpp.
  34. //-------------------------------------------------------------
  35. extern GpStatus
  36. GetMajorAndMinorAxis(
  37. REAL* majorR,
  38. REAL* minorR,
  39. const GpMatrix* matrix
  40. );
  41. GpEndCapCreator::GpEndCapCreator(
  42. GpPath *path,
  43. DpPen *pen,
  44. const GpMatrix *m,
  45. REAL dpi_x,
  46. REAL dpi_y,
  47. bool antialias
  48. )
  49. {
  50. Path = path;
  51. Pen = pen;
  52. if(m) {XForm = *m;}
  53. XForm.Prepend(pen->Xform);
  54. DpiX = dpi_x;
  55. DpiY = dpi_y;
  56. Antialias = antialias;
  57. StartCap = NULL;
  58. EndCap = NULL;
  59. switch(Pen->StartCap)
  60. {
  61. case LineCapCustom:
  62. StartCap = static_cast<GpCustomLineCap*>(Pen->CustomStartCap);
  63. break;
  64. case LineCapArrowAnchor:
  65. StartCap = GpEndCapCreator::ReferenceArrowAnchor();
  66. break;
  67. case LineCapDiamondAnchor:
  68. StartCap = GpEndCapCreator::ReferenceDiamondAnchor();
  69. break;
  70. case LineCapRoundAnchor:
  71. StartCap = GpEndCapCreator::ReferenceRoundAnchor();
  72. break;
  73. case LineCapSquareAnchor:
  74. StartCap = GpEndCapCreator::ReferenceSquareAnchor();
  75. break;
  76. // The non-anchor caps are handled by the widener.
  77. };
  78. switch(Pen->EndCap)
  79. {
  80. case LineCapCustom:
  81. EndCap = static_cast<GpCustomLineCap*>(Pen->CustomEndCap);
  82. break;
  83. case LineCapArrowAnchor:
  84. EndCap = GpEndCapCreator::ReferenceArrowAnchor();
  85. break;
  86. case LineCapDiamondAnchor:
  87. EndCap = GpEndCapCreator::ReferenceDiamondAnchor();
  88. break;
  89. case LineCapRoundAnchor:
  90. EndCap = GpEndCapCreator::ReferenceRoundAnchor();
  91. break;
  92. case LineCapSquareAnchor:
  93. EndCap = GpEndCapCreator::ReferenceSquareAnchor();
  94. break;
  95. // The non-anchor caps are handled by the widener.
  96. };
  97. // If we're flipped in the X or Y direction (but not both),
  98. // reverse the fill and stroke paths so that the winding
  99. // mode will be correct.
  100. if (pen->Xform.GetDeterminant() < 0)
  101. {
  102. if (StartCap)
  103. {
  104. StartCap->ReverseFillPath();
  105. StartCap->ReverseStrokePath();
  106. }
  107. if (EndCap)
  108. {
  109. EndCap->ReverseFillPath();
  110. EndCap->ReverseStrokePath();
  111. }
  112. }
  113. }
  114. /**************************************************************************\
  115. *
  116. * Function Description:
  117. *
  118. * This function will return true if the GpEndCapCreator is required for
  119. * the given pen. If the pen only has simple endcaps, then the
  120. * GpEndCapCreator can skipped.
  121. *
  122. * The GpEndCapCreator is used for creating Anchor and/or Custom caps.
  123. * LineCap- Flat, Round, Square and Triangle are handled directly by the
  124. * widener.
  125. *
  126. * Revision History:
  127. *
  128. * 11/10/2000 asecchia
  129. * Created it
  130. *
  131. \**************************************************************************/
  132. bool GpEndCapCreator::PenNeedsEndCapCreator(const DpPen *pen)
  133. {
  134. return (
  135. (pen->StartCap == LineCapCustom) ||
  136. (pen->EndCap == LineCapCustom) ||
  137. ((pen->StartCap & LineCapAnchorMask) != 0) ||
  138. ((pen->EndCap & LineCapAnchorMask) != 0)
  139. );
  140. }
  141. GpEndCapCreator::~GpEndCapCreator()
  142. {
  143. // If we allocated memory for temporary custom caps, then
  144. // throw that memory away.
  145. if(Pen->StartCap != LineCapCustom)
  146. {
  147. delete StartCap;
  148. StartCap = NULL;
  149. }
  150. if(Pen->EndCap != LineCapCustom)
  151. {
  152. delete EndCap;
  153. EndCap = NULL;
  154. }
  155. }
  156. /**************************************************************************\
  157. *
  158. * Function Description:
  159. *
  160. * Creates a reference GpCustomLineCap representing an ArrowAnchor.
  161. * This is an equilateral triangle with edge equal to 2. This means
  162. * that the scaling will create a 2xStrokeWidth cap edge length.
  163. *
  164. * Revision History:
  165. *
  166. * 10/08/2000 asecchia
  167. * Created it
  168. *
  169. \**************************************************************************/
  170. GpCustomLineCap *GpEndCapCreator::ReferenceArrowAnchor()
  171. {
  172. // the square root of 3
  173. const REAL root3 = 1.732050808f;
  174. // Anti-clockwise definition of an equilateral triangle of side length 2.0f
  175. // with a vertex on the origin and axis extending along the negative
  176. // y axis.
  177. const GpPointF points[3] = {
  178. GpPointF(0.0f, 0.0f),
  179. GpPointF(-1.0f, -root3),
  180. GpPointF(1.0f, -root3)
  181. };
  182. GpPath arrowAnchor(FillModeWinding);
  183. arrowAnchor.AddPolygon(points, 3);
  184. // Create the custom line cap. If it fails it will return NULL.
  185. GpCustomLineCap *cap = new GpCustomLineCap(&arrowAnchor, NULL);
  186. if(cap)
  187. {
  188. cap->SetBaseInset(1.0f);
  189. }
  190. return cap;
  191. }
  192. /**************************************************************************\
  193. *
  194. * Function Description:
  195. *
  196. * Creates a reference GpCustomLineCap representing a DiamondAnchor.
  197. * This is a square centered on the end point of the path with it's
  198. * diagonal along the axis of the spine.
  199. *
  200. * Revision History:
  201. *
  202. * 10/08/2000 asecchia
  203. * Created it
  204. *
  205. \**************************************************************************/
  206. GpCustomLineCap *GpEndCapCreator::ReferenceDiamondAnchor()
  207. {
  208. // Anti-clockwise definition of a square of diagonal size 2.0f
  209. // with the center on the origin and axis extending along the negative
  210. // y axis.
  211. const GpPointF points[4] = {
  212. GpPointF(0.0f, 1.0f),
  213. GpPointF(-1.0f, 0.0f),
  214. GpPointF(0.0f, -1.0f),
  215. GpPointF(1.0f, 0.0f)
  216. };
  217. GpPath diamondAnchor(FillModeWinding);
  218. diamondAnchor.AddPolygon(points, 4);
  219. // Create the custom line cap. If it fails it will return NULL.
  220. GpCustomLineCap *cap = new GpCustomLineCap(&diamondAnchor, NULL);
  221. if(cap)
  222. {
  223. cap->SetBaseInset(0.0f);
  224. }
  225. return cap;
  226. }
  227. /**************************************************************************\
  228. *
  229. * Function Description:
  230. *
  231. * Creates a reference GpCustomLineCap representing a SquareAnchor.
  232. * This is a square that has a 2 unit long diagonal and is centered on
  233. * the end point of the path.
  234. *
  235. * Revision History:
  236. *
  237. * 10/17/2000 peterost
  238. * Created it
  239. *
  240. \**************************************************************************/
  241. GpCustomLineCap *GpEndCapCreator::ReferenceSquareAnchor()
  242. {
  243. const REAL halfRoot2 = 0.7071068f;
  244. const GpPointF points[4] = {
  245. GpPointF(-halfRoot2, -halfRoot2),
  246. GpPointF(halfRoot2, -halfRoot2),
  247. GpPointF(halfRoot2, halfRoot2),
  248. GpPointF(-halfRoot2, halfRoot2)
  249. };
  250. GpPath squareAnchor(FillModeWinding);
  251. squareAnchor.AddPolygon(points, 4);
  252. // Create the custom line cap. If it fails it will return NULL.
  253. GpCustomLineCap *cap = new GpCustomLineCap(&squareAnchor, NULL);
  254. if(cap)
  255. {
  256. cap->SetBaseInset(0.0f);
  257. }
  258. return cap;
  259. }
  260. /**************************************************************************\
  261. *
  262. * Function Description:
  263. *
  264. * Creates a reference GpCustomLineCap representing a RoundAnchor.
  265. * This is a circle centered on the end point of the path.
  266. *
  267. * Revision History:
  268. *
  269. * 10/08/2000 asecchia
  270. * Created it
  271. *
  272. \**************************************************************************/
  273. GpCustomLineCap *GpEndCapCreator::ReferenceRoundAnchor()
  274. {
  275. // Create the custom line cap. If it fails it will return NULL.
  276. GpPath roundAnchor(FillModeWinding);
  277. roundAnchor.AddEllipse(-1.0f, -1.0f, 2.0f, 2.0f);
  278. GpCustomLineCap *cap = new GpCustomLineCap(&roundAnchor, NULL);
  279. if(cap)
  280. {
  281. cap->SetBaseInset(0.0f);
  282. }
  283. return cap;
  284. }
  285. /**************************************************************************\
  286. *
  287. * Function Description:
  288. *
  289. * ComputeCapGradient.
  290. *
  291. * Compute the correct gradient for a line cap of a given length.
  292. * Work out the direction of the cap from the list of input
  293. * points in the path and the length of the cap.
  294. * Simply put, the direction is the line segment formed by
  295. * the end point of the path and the first intersection along the
  296. * path with a circle of length "length" and centered at the
  297. * first point of the path.
  298. *
  299. * Arguments:
  300. *
  301. * GpIterator<GpPointF> &pointIterator,
  302. * BYTE *types,
  303. * IN REAL lengthSquared, length of the cap squared.
  304. * IN baseInset, amount to draw into the shape.
  305. * OUT GpVector2D *grad, output gradient vector
  306. *
  307. *
  308. * Revision History:
  309. *
  310. * 08/23/00 asecchia
  311. * Created it
  312. *
  313. \**************************************************************************/
  314. void GpEndCapCreator::ComputeCapGradient(
  315. GpIterator<GpPointF> &pointIterator,
  316. BYTE *types,
  317. IN REAL lengthSquared,
  318. IN REAL baseInset,
  319. OUT GpVector2D *grad
  320. )
  321. {
  322. // Start at the beginning of the iterator (end of the list of
  323. // points if isStartCap is FALSE)
  324. GpPointF *endPoint = pointIterator.CurrentItem();
  325. GpPointF *curPoint = endPoint;
  326. INT index;
  327. bool intersectionFound = false;
  328. bool priorDeletion = false;
  329. while(!pointIterator.IsDone())
  330. {
  331. curPoint = pointIterator.CurrentItem();
  332. if(lengthSquared < distance_squared(*curPoint, *endPoint))
  333. {
  334. intersectionFound = true;
  335. break;
  336. }
  337. // Mark this point for deletion by the trimming algorithm.
  338. index = pointIterator.CurrentIndex();
  339. // Check to see if anyone already deleted this segment.
  340. // PathPointTypeInternalUse is the marked-for-deletion flag.
  341. priorDeletion = (types[index] & PathPointTypeInternalUse) ==
  342. PathPointTypeInternalUse;
  343. types[index] |= PathPointTypeInternalUse;
  344. pointIterator.Next();
  345. }
  346. // Now we have the segment that intersects the base of the arrow.
  347. // or the last segment.
  348. pointIterator.Prev();
  349. // if we couldn't get the Prev, then we were at the beginning.
  350. #if DBG
  351. if(pointIterator.IsDone())
  352. {
  353. ONCE(WARNING(("not enough points in array")));
  354. }
  355. #endif
  356. // If the intersection was not found we have marked the entire subpath
  357. // for deletion.
  358. if(intersectionFound && !priorDeletion)
  359. {
  360. // We overagressively marked this point for deletion,
  361. // instead of deleting this point, we're going to move it.
  362. // Note: we may have found an intersection point in a segment
  363. // that has already been marked for deletion. Checking priorDeletion
  364. // here ensures that we don't incorrectly undelete this point.
  365. index = pointIterator.CurrentIndex();
  366. // PathPointTypeInternalUse is the marked-for-deletion flag.
  367. types[index] &= ~PathPointTypeInternalUse;
  368. }
  369. GpPointF *prevPoint = pointIterator.CurrentItem();
  370. GpPointF intersectionPoint;
  371. if(!intersect_circle_line(
  372. *endPoint, // center
  373. lengthSquared, // radius^2
  374. *curPoint, // P0
  375. *prevPoint, // P1
  376. &intersectionPoint
  377. ))
  378. {
  379. // If there is no intersection, then the line segment is likely too
  380. // short, so just take the previous point as the intersection.
  381. // This is our best guess and in this case will give us the slope from
  382. // the start to end point as the cap direction.
  383. intersectionPoint.X = prevPoint->X;
  384. intersectionPoint.Y = prevPoint->Y;
  385. }
  386. // Compute the gradient - and normalize the vector.
  387. *grad = intersectionPoint - *endPoint;
  388. grad->Normalize();
  389. // Update the point in the path directly.
  390. GpVector2D v = *endPoint - intersectionPoint;
  391. *prevPoint = intersectionPoint + (v*(1.0f-baseInset));
  392. }
  393. /**************************************************************************\
  394. *
  395. * Function Description:
  396. *
  397. * This creates a path containing all the custom end caps for all
  398. * the open subpaths in the input path.
  399. *
  400. * Return
  401. *
  402. * Status
  403. *
  404. * Arguments:
  405. *
  406. * [OUT] caps -- this is where we put the caps we generate
  407. *
  408. * Created:
  409. *
  410. * 10/05/2000 asecchia
  411. * created it.
  412. *
  413. \**************************************************************************/
  414. GpStatus
  415. GpEndCapCreator::CreateCapPath(GpPath **caps)
  416. {
  417. // Validate our input data.
  418. ASSERT(Pen != NULL);
  419. ASSERT(Path != NULL);
  420. ASSERT(caps != NULL);
  421. ASSERT(*caps == NULL);
  422. // Create our cap path.
  423. *caps = new GpPath(FillModeWinding);
  424. if(*caps==NULL)
  425. {
  426. return OutOfMemory;
  427. }
  428. // Create a path points iterator because our GpPath doesn't know how
  429. // to iterate over its own data *sigh*
  430. GpPathPointIterator pathIterator(
  431. const_cast<GpPointF*>(Path->GetPathPoints()),
  432. const_cast<BYTE*>(Path->GetPathTypes()),
  433. Path->GetPointCount()
  434. );
  435. GpSubpathIterator subpathIterator(&pathIterator);
  436. // Loop through all the available subpaths.
  437. while(!subpathIterator.IsDone())
  438. {
  439. // Compute the length of the subpath.
  440. INT startIndex = subpathIterator.CurrentIndex();
  441. GpPointF *points = subpathIterator.CurrentItem();
  442. BYTE *types = subpathIterator.CurrentType();
  443. subpathIterator.Next();
  444. INT elementCount = subpathIterator.CurrentIndex() - startIndex;
  445. // Work out if it's a closed subpath.
  446. // Leave the subpath iterator in the same state.
  447. pathIterator.Prev();
  448. bool isClosed =
  449. ((*(pathIterator.CurrentType()) & PathPointTypeCloseSubpath) ==
  450. PathPointTypeCloseSubpath);
  451. pathIterator.Next();
  452. // only want to add end caps if this is an open subpath.
  453. if(!isClosed)
  454. {
  455. GpPath *startCap = NULL;
  456. GpPath *endCap = NULL;
  457. // Create the cap using the points and types
  458. GetCapsForSubpath(
  459. &startCap,
  460. &endCap,
  461. points,
  462. types,
  463. elementCount
  464. );
  465. // Add the cap to our caps path.
  466. (*caps)->AddPath(startCap, FALSE);
  467. (*caps)->AddPath(endCap, FALSE);
  468. // Clean up the temporary caps for the next iteration.
  469. delete startCap;
  470. delete endCap;
  471. }
  472. }
  473. return Ok;
  474. }
  475. /**************************************************************************\
  476. *
  477. * Function Description:
  478. *
  479. * This takes a pen and sets it up to match the internal Pen, but modified
  480. * to support stroking the StrokeCap. E.g. the caps are removed to avoid
  481. * recursive compound capping etc.
  482. *
  483. * Arguments:
  484. *
  485. * [OUT] pen -- this is where we put the pen we generate
  486. * [IN] customCap -- input custom cap.
  487. *
  488. * Created:
  489. *
  490. * 10/09/2000 asecchia
  491. * rewrote it.
  492. *
  493. \**************************************************************************/
  494. VOID GpEndCapCreator::PrepareDpPenForCustomCap(
  495. DpPen* pen,
  496. const GpCustomLineCap* customCap
  497. ) const
  498. {
  499. ASSERT(pen);
  500. *pen = *Pen;
  501. pen->StartCap = LineCapFlat;
  502. pen->EndCap = LineCapFlat;
  503. pen->Join = LineJoinMiter;
  504. pen->MiterLimit = 10;
  505. pen->PenAlignment = PenAlignmentCenter;
  506. pen->DashStyle = DashStyleSolid;
  507. pen->DashCap = LineCapFlat;
  508. pen->DashCount = 0;
  509. pen->DashOffset = 0;
  510. pen->DashArray = NULL;
  511. pen->CompoundCount = 0;
  512. pen->CompoundArray = NULL;
  513. pen->CustomEndCap = NULL;
  514. pen->CustomStartCap = NULL;
  515. GpLineCap startCap, endCap;
  516. GpLineJoin lineJoin;
  517. if(customCap)
  518. {
  519. REAL widthScale;
  520. customCap->GetStrokeCaps(&startCap, &endCap);
  521. customCap->GetStrokeJoin(&lineJoin);
  522. customCap->GetWidthScale(&widthScale);
  523. pen->Width *= widthScale;
  524. pen->StartCap = startCap;
  525. pen->EndCap = endCap;
  526. pen->Join = lineJoin;
  527. }
  528. }
  529. GpStatus
  530. GpEndCapCreator::SetCustomFillCaps(
  531. GpCustomLineCap* customStartCap,
  532. GpCustomLineCap* customEndCap,
  533. const GpPointF& startPoint,
  534. const GpPointF& endPoint,
  535. const GpPointF *centerPoints,
  536. const BYTE *centerTypes,
  537. INT centerPointCount,
  538. DynPointFArray *startCapPoints,
  539. DynPointFArray *endCapPoints,
  540. DynByteArray *startCapTypes,
  541. DynByteArray *endCapTypes
  542. )
  543. {
  544. GpStatus status = Ok;
  545. startCapPoints->Reset(FALSE);
  546. startCapTypes->Reset(FALSE);
  547. endCapPoints->Reset(FALSE);
  548. endCapTypes->Reset(FALSE);
  549. INT count;
  550. GpPointF tangent;
  551. GpPointF* points;
  552. BYTE* types;
  553. REAL width, widthScale;
  554. // Get minimum line width based on the transform currently in effect.
  555. REAL majorR, minorR, unitScale;
  556. GetMajorAndMinorAxis(&majorR, &minorR, &XForm);
  557. unitScale = min(majorR, minorR);
  558. if(customStartCap)
  559. {
  560. // Get the start cap and inset of the base start cap.
  561. count = customStartCap->GetFillPointCount();
  562. if(count > 0)
  563. {
  564. points = startCapPoints->AddMultiple(count);
  565. types = startCapTypes->AddMultiple(count);
  566. if(!points || !types)
  567. {
  568. startCapPoints->Reset(FALSE);
  569. startCapTypes->Reset(FALSE);
  570. status = OutOfMemory;
  571. }
  572. if(status == Ok)
  573. {
  574. customStartCap->GetWidthScale(&widthScale);
  575. width = Pen->Width*widthScale;
  576. REAL length = customStartCap->GetFillLength();
  577. // Compute the base inset. Divide by the length to get a
  578. // number between 0 and 1. 0=no inset, 1=inset to the full
  579. // length of the cap.
  580. REAL inset;
  581. customStartCap->GetBaseInset(&inset);
  582. if(REALABS(length) < REAL_EPSILON)
  583. {
  584. inset = 0.0f;
  585. }
  586. else
  587. {
  588. inset /= length;
  589. }
  590. length *= max(width, 1.0f/unitScale);
  591. // Compute the gradient of the cap.
  592. GpArrayIterator<GpPointF> pointIterator(
  593. const_cast<GpPointF*>(centerPoints),
  594. centerPointCount
  595. );
  596. GpVector2D gradient;
  597. ComputeCapGradient(
  598. pointIterator,
  599. const_cast<BYTE*>(centerTypes),
  600. length*length,
  601. inset,
  602. &gradient // OUT parameters
  603. );
  604. tangent.X = -gradient.X;
  605. tangent.Y = -gradient.Y;
  606. // Move start point left or right to account for inset
  607. // pens, if needed.
  608. GpPointF start;
  609. start.X = startPoint.X;
  610. start.Y = startPoint.Y;
  611. customStartCap->GetTransformedFillCap(
  612. points,
  613. types,
  614. count,
  615. start,
  616. tangent,
  617. width,
  618. 2.0f / unitScale
  619. );
  620. }
  621. }
  622. }
  623. if(status == Ok && customEndCap)
  624. {
  625. // Get the start cap and inset of the base start cap.
  626. count = customEndCap->GetFillPointCount();
  627. if(count > 0)
  628. {
  629. points = endCapPoints->AddMultiple(count);
  630. types = endCapTypes->AddMultiple(count);
  631. if(!points || !types)
  632. {
  633. endCapPoints->Reset(FALSE);
  634. endCapTypes->Reset(FALSE);
  635. status = OutOfMemory;
  636. }
  637. if(status == Ok)
  638. {
  639. customEndCap->GetWidthScale(&widthScale);
  640. width = Pen->Width*widthScale;
  641. REAL length = customEndCap->GetFillLength();
  642. // Compute the base inset. Divide by the length to get a
  643. // number between 0 and 1. 0=no inset, 1=inset to the full
  644. // length of the cap.
  645. REAL inset;
  646. customEndCap->GetBaseInset(&inset);
  647. if(REALABS(length) < REAL_EPSILON)
  648. {
  649. inset = 0.0f;
  650. }
  651. else
  652. {
  653. inset /= length;
  654. }
  655. length *= max(width, 1.0f/unitScale);
  656. // Compute the gradient of the cap.
  657. GpArrayIterator<GpPointF> pointIterator(
  658. const_cast<GpPointF*>(centerPoints),
  659. centerPointCount
  660. );
  661. GpReverseIterator<GpPointF> pointReverse(&pointIterator);
  662. pointReverse.SeekFirst();
  663. GpVector2D gradient;
  664. ComputeCapGradient(
  665. pointReverse,
  666. const_cast<BYTE*>(centerTypes),
  667. length*length,
  668. inset,
  669. &gradient // OUT parameters
  670. );
  671. tangent.X = - gradient.X;
  672. tangent.Y = - gradient.Y;
  673. // Move end point left or right to account for inset
  674. // pens, if needed.
  675. GpPointF end;
  676. end.X = endPoint.X;
  677. end.Y = endPoint.Y;
  678. customEndCap->GetTransformedFillCap(
  679. points,
  680. types,
  681. count,
  682. end,
  683. tangent,
  684. width,
  685. 2.0f / unitScale
  686. );
  687. }
  688. }
  689. }
  690. return status;
  691. }
  692. GpStatus
  693. GpEndCapCreator::SetCustomStrokeCaps(
  694. GpCustomLineCap* customStartCap,
  695. GpCustomLineCap* customEndCap,
  696. const GpPointF& startPoint,
  697. const GpPointF& endPoint,
  698. const GpPointF *centerPoints,
  699. const BYTE *centerTypes,
  700. INT centerPointCount,
  701. DynPointFArray *startCapPoints,
  702. DynPointFArray *endCapPoints,
  703. DynByteArray *startCapTypes,
  704. DynByteArray *endCapTypes
  705. )
  706. {
  707. GpStatus status = Ok;
  708. GpPointF* points = NULL;
  709. BYTE* types = NULL;
  710. INT count;
  711. GpPointF tangent, start, end;
  712. INT startCount = 0;
  713. INT endCount = 0;
  714. if(customStartCap)
  715. {
  716. startCount = customStartCap->GetStrokePointCount();
  717. }
  718. if(customEndCap)
  719. {
  720. endCount = customEndCap->GetStrokePointCount();
  721. }
  722. INT maxCount = max(startCount, endCount);
  723. if(maxCount <= 0)
  724. {
  725. return Ok;
  726. }
  727. points = (GpPointF*) GpMalloc(maxCount*sizeof(GpPointF));
  728. types = (BYTE*) GpMalloc(maxCount);
  729. if(!points || !types)
  730. {
  731. GpFree(points);
  732. GpFree(types);
  733. return OutOfMemory;
  734. }
  735. DpPen pen;
  736. GpPointF* widenedPts;
  737. INT widenedCount;
  738. REAL widthScale, width;
  739. if(customStartCap && startCount > 0)
  740. {
  741. startCapPoints->Reset(FALSE);
  742. startCapTypes->Reset(FALSE);
  743. customStartCap->GetWidthScale(&widthScale);
  744. width = Pen->Width*widthScale;
  745. REAL length = customStartCap->GetStrokeLength();
  746. // Handle the case of a non-closed stroke path
  747. // in this case the length is typically zero.
  748. if(REALABS(length)<REAL_EPSILON)
  749. {
  750. length = 1.0f;
  751. }
  752. // Compute the base inset. Divide by the length to get a
  753. // number between 0 and 1. 0=no inset, 1=inset to the full
  754. // length of the cap.
  755. REAL inset;
  756. customStartCap->GetBaseInset(&inset);
  757. inset /= length;
  758. length *= width;
  759. // Compute the gradient of the cap.
  760. GpArrayIterator<GpPointF> pointIterator(
  761. const_cast<GpPointF*>(centerPoints),
  762. centerPointCount
  763. );
  764. GpVector2D gradient;
  765. ComputeCapGradient(
  766. pointIterator,
  767. const_cast<BYTE*>(centerTypes),
  768. length*length,
  769. inset,
  770. &gradient // OUT parameters
  771. );
  772. tangent.X = -gradient.X;
  773. tangent.Y = -gradient.Y;
  774. // Move start point left or right to account for inset
  775. // pens, if needed.
  776. GpPointF start;
  777. start.X = startPoint.X;
  778. start.Y = startPoint.Y;
  779. customStartCap->GetTransformedStrokeCap(
  780. maxCount,
  781. &points,
  782. &types,
  783. &startCount,
  784. start,
  785. tangent,
  786. width,
  787. width
  788. );
  789. PrepareDpPenForCustomCap(&pen, customStartCap);
  790. GpPathWidener widener(
  791. points,
  792. types,
  793. startCount,
  794. &pen,
  795. &XForm,
  796. DpiX, // widener doesn't use these.
  797. DpiY,
  798. Antialias
  799. );
  800. widener.Widen(startCapPoints, startCapTypes);
  801. }
  802. if(customEndCap && endCount > 0)
  803. {
  804. endCapPoints->Reset(FALSE);
  805. endCapTypes->Reset(FALSE);
  806. customEndCap->GetWidthScale(&widthScale);
  807. width = Pen->Width*widthScale;
  808. REAL length = customEndCap->GetStrokeLength();
  809. // Handle the case of a non-closed stroke path
  810. // in this case the length is typically zero.
  811. if(REALABS(length)<REAL_EPSILON)
  812. {
  813. length = 1.0f;
  814. }
  815. // Compute the base inset. Divide by the length to get a
  816. // number between 0 and 1. 0=no inset, 1=inset to the full
  817. // length of the cap.
  818. REAL inset;
  819. customEndCap->GetBaseInset(&inset);
  820. inset /= length;
  821. length *= width;
  822. // Compute the gradient of the cap.
  823. GpArrayIterator<GpPointF> pointIterator(
  824. const_cast<GpPointF*>(centerPoints),
  825. centerPointCount
  826. );
  827. GpReverseIterator<GpPointF> pointReverse(&pointIterator);
  828. pointReverse.SeekFirst();
  829. GpVector2D gradient;
  830. ComputeCapGradient(
  831. pointReverse,
  832. const_cast<BYTE*>(centerTypes),
  833. length*length,
  834. inset,
  835. &gradient // OUT parameter
  836. );
  837. tangent.X = - gradient.X;
  838. tangent.Y = - gradient.Y;
  839. // Move end point left or right to account for inset
  840. // pens, if needed.
  841. GpPointF end;
  842. end.X = endPoint.X;
  843. end.Y = endPoint.Y;
  844. customEndCap->GetTransformedStrokeCap(
  845. maxCount,
  846. &points,
  847. &types,
  848. &endCount,
  849. end,
  850. tangent,
  851. width,
  852. width
  853. );
  854. PrepareDpPenForCustomCap(&pen, customEndCap);
  855. GpPathWidener widener(
  856. points,
  857. types,
  858. endCount,
  859. &pen,
  860. &XForm,
  861. DpiX, // widener doesn't use these.
  862. DpiY,
  863. Antialias
  864. );
  865. widener.Widen(endCapPoints, endCapTypes);
  866. }
  867. GpFree(points);
  868. GpFree(types);
  869. return status;
  870. }
  871. /**************************************************************************\
  872. *
  873. * Function Description:
  874. *
  875. * This creates and returns two GpPaths containing the start and end cap.
  876. * The two caps are correctly positioned and scaled.
  877. *
  878. * Return
  879. *
  880. * Status
  881. *
  882. * Arguments:
  883. *
  884. * [OUT] startCapPath, endCapPath
  885. *
  886. * Created:
  887. *
  888. * 10/05/2000 asecchia
  889. * created it.
  890. *
  891. \**************************************************************************/
  892. GpStatus
  893. GpEndCapCreator::GetCapsForSubpath(
  894. GpPath **startCapPath,
  895. GpPath **endCapPath,
  896. GpPointF *centerPoints,
  897. BYTE *centerTypes,
  898. INT centerCount
  899. )
  900. {
  901. // Validate our input parameters.
  902. ASSERT(startCapPath != NULL);
  903. ASSERT(endCapPath != NULL);
  904. ASSERT(*startCapPath == NULL);
  905. ASSERT(*endCapPath == NULL);
  906. DynPointFArray startCapPoints;
  907. DynPointFArray endCapPoints;
  908. DynByteArray startCapTypes;
  909. DynByteArray endCapTypes;
  910. GpPointF startPoint, endPoint;
  911. startPoint = *(centerPoints);
  912. endPoint = *(centerPoints + centerCount - 1);
  913. GpStatus status = Ok;
  914. if(StartCap || EndCap)
  915. {
  916. status = SetCustomFillCaps(
  917. StartCap,
  918. EndCap,
  919. startPoint,
  920. endPoint,
  921. centerPoints,
  922. centerTypes,
  923. centerCount,
  924. &startCapPoints,
  925. &endCapPoints,
  926. &startCapTypes,
  927. &endCapTypes
  928. );
  929. if(status == Ok)
  930. {
  931. status = SetCustomStrokeCaps(
  932. StartCap,
  933. EndCap,
  934. startPoint,
  935. endPoint,
  936. centerPoints,
  937. centerTypes,
  938. centerCount,
  939. &startCapPoints,
  940. &endCapPoints,
  941. &startCapTypes,
  942. &endCapTypes
  943. );
  944. }
  945. }
  946. if(startCapPoints.GetCount() > 0)
  947. {
  948. *startCapPath = new GpPath(
  949. startCapPoints.GetDataBuffer(),
  950. startCapTypes.GetDataBuffer(),
  951. startCapPoints.GetCount()
  952. );
  953. if(*startCapPath == NULL)
  954. {
  955. status = OutOfMemory;
  956. }
  957. }
  958. if(endCapPoints.GetCount() > 0)
  959. {
  960. *endCapPath = new GpPath(
  961. endCapPoints.GetDataBuffer(),
  962. endCapTypes.GetDataBuffer(),
  963. endCapPoints.GetCount()
  964. );
  965. if(*endCapPath == NULL)
  966. {
  967. status = OutOfMemory;
  968. }
  969. }
  970. if(status != Ok)
  971. {
  972. delete *startCapPath;
  973. delete *endCapPath;
  974. *startCapPath = NULL;
  975. *endCapPath = NULL;
  976. status = OutOfMemory;
  977. }
  978. return status;
  979. }