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.

5668 lines
142 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1998 Microsoft Corporation
  4. *
  5. * Module Name:
  6. *
  7. * path.cpp
  8. *
  9. * Abstract:
  10. *
  11. * Implementation of the GpPath and DpPath classes
  12. *
  13. * Revision History:
  14. *
  15. * 12/11/1998 davidx
  16. * Add path functions.
  17. *
  18. * 12/07/1998 davidx
  19. * Initial placeholders.
  20. *
  21. \**************************************************************************/
  22. #include "precomp.hpp"
  23. //-------------------------------------------------------------
  24. // ReversePath(), CombinePaths(), CalculateGradientArray(), and
  25. // GetMajorAndMinorAxis(), and GetFastAngle are defined in
  26. // PathWidener.cpp.
  27. //-------------------------------------------------------------
  28. extern GpStatus
  29. ReversePath(
  30. INT count,
  31. GpPointF* points,
  32. BYTE* types
  33. );
  34. extern INT
  35. CombinePaths(
  36. INT count,
  37. GpPointF* points,
  38. BYTE* types,
  39. INT count1,
  40. const GpPointF* points1,
  41. const BYTE* types1,
  42. BOOL forward1,
  43. INT count2,
  44. const GpPointF* points2,
  45. const BYTE* types2,
  46. BOOL forward2,
  47. BOOL connect
  48. );
  49. extern GpStatus
  50. CalculateGradientArray(
  51. GpPointF* grad,
  52. REAL* distances,
  53. const GpPointF* points,
  54. INT count
  55. );
  56. extern GpStatus
  57. GetMajorAndMinorAxis(
  58. REAL* majorR,
  59. REAL* minorR,
  60. const GpMatrix* matrix
  61. );
  62. VOID NormalizeAngle(REAL* angle, REAL width, REAL height);
  63. INT NormalizeArcAngles(
  64. REAL* startAngle,
  65. REAL* sweepAngle,
  66. REAL width,
  67. REAL height
  68. );
  69. // Note that this is different from GpPathData.
  70. class MetaPathData : public ObjectData
  71. {
  72. public:
  73. UINT32 Count;
  74. INT32 Flags;
  75. };
  76. /**************************************************************************\
  77. *
  78. * Function Description:
  79. *
  80. * Get the path data.
  81. *
  82. * Arguments:
  83. *
  84. * [IN] dataBuffer - fill this buffer with the data
  85. * [IN/OUT] size - IN - size of buffer; OUT - number bytes written
  86. *
  87. * Return Value:
  88. *
  89. * GpStatus - Ok or error code
  90. *
  91. * Created:
  92. *
  93. * 9/13/1999 DCurtis
  94. *
  95. \**************************************************************************/
  96. GpStatus
  97. DpPath::GetData(
  98. IStream * stream
  99. ) const
  100. {
  101. ASSERT (stream != NULL);
  102. INT count = Points.GetCount();
  103. MetafilePointData pointData(Points.GetDataBuffer(), count);
  104. UINT pointsSize = pointData.GetDataSize();
  105. INT flags = pointData.GetFlags();
  106. if (FillMode == FillModeWinding)
  107. {
  108. flags |= GDIP_EPRFLAGS_WINDINGFILL;
  109. }
  110. MetaPathData pathData;
  111. pathData.Count = count;
  112. pathData.Flags = flags;
  113. stream->Write(&pathData, sizeof(pathData), NULL);
  114. stream->Write(pointData.GetData(), pointsSize, NULL);
  115. stream->Write(Types.GetDataBuffer(), count, NULL);
  116. // align
  117. if ((count & 0x03) != 0)
  118. {
  119. INT pad = 0;
  120. stream->Write(&pad, 4 - (count & 0x03), NULL);
  121. }
  122. return Ok;
  123. }
  124. UINT
  125. DpPath::GetDataSize() const
  126. {
  127. INT count = Points.GetCount();
  128. MetafilePointData pointData(Points.GetDataBuffer(), count);
  129. UINT pointsSize = pointData.GetDataSize();
  130. UINT dataSize = sizeof(MetaPathData) + pointsSize + count;
  131. return ((dataSize + 3) & (~3)); // align
  132. }
  133. /**************************************************************************\
  134. *
  135. * Function Description:
  136. *
  137. * Read the path object from memory.
  138. *
  139. * Arguments:
  140. *
  141. * [IN] memory - the data that was read from the stream
  142. * [IN] size - the size of the memory data
  143. *
  144. * Return Value:
  145. *
  146. * GpStatus - Ok or failure status
  147. *
  148. * Created:
  149. *
  150. * 4/26/1999 DCurtis
  151. *
  152. \**************************************************************************/
  153. GpStatus
  154. DpPath::SetData(
  155. const BYTE * dataBuffer,
  156. UINT size
  157. )
  158. {
  159. Points.Reset();
  160. Types.Reset();
  161. if (dataBuffer == NULL)
  162. {
  163. WARNING(("dataBuffer is NULL"));
  164. return InvalidParameter;
  165. }
  166. if (size >= sizeof(MetaPathData))
  167. {
  168. const MetaPathData * pathData = reinterpret_cast<const MetaPathData *>(dataBuffer);
  169. if (!pathData->MajorVersionMatches())
  170. {
  171. WARNING(("Version number mismatch"));
  172. return InvalidParameter;
  173. }
  174. InitDefaultState(::GetFillMode(pathData->Flags));
  175. SetValid(TRUE);
  176. INT count = pathData->Count;
  177. if (count > 0)
  178. {
  179. UINT pointDataSize;
  180. if ((pathData->Flags & GDIP_EPRFLAGS_COMPRESSED) != 0)
  181. {
  182. pointDataSize = count * sizeof(GpPoint16);
  183. }
  184. else
  185. {
  186. pointDataSize = count * sizeof(GpPointF);
  187. }
  188. if (size >= sizeof(MetaPathData) + count + pointDataSize)
  189. {
  190. GpPointF * points = Points.AddMultiple(count);
  191. BYTE * types = Types.AddMultiple(count);
  192. const BYTE * typeData;
  193. const BYTE * pointData = dataBuffer + sizeof(MetaPathData);
  194. if ((points != NULL) && (types != NULL))
  195. {
  196. if ((pathData->Flags & GDIP_EPRFLAGS_COMPRESSED) != 0)
  197. {
  198. BYTE * tmp = NULL;
  199. ::GetPointsForPlayback(
  200. pointData,
  201. size - (sizeof(MetaPathData) + count),
  202. count,
  203. pathData->Flags,
  204. sizeof(GpPointF) * count,
  205. (BYTE *)points,
  206. tmp);
  207. typeData = pointData + (count * 4);
  208. }
  209. else
  210. {
  211. GpMemcpy(points, pointData, count * sizeof(points[0]));
  212. typeData = pointData + (count * sizeof(points[0]));
  213. }
  214. GpMemcpy(types, typeData, count);
  215. if (ValidatePathTypes(types, count, &SubpathCount, &HasBezier))
  216. {
  217. UpdateUid();
  218. return Ok;
  219. }
  220. }
  221. }
  222. else
  223. {
  224. WARNING(("size is too small"));
  225. }
  226. }
  227. }
  228. else
  229. {
  230. WARNING(("size is too small"));
  231. }
  232. SetValid(FALSE);
  233. return GenericError;
  234. }
  235. /**************************************************************************\
  236. *
  237. * Function Description:
  238. *
  239. * Construct a new GpPath object using the specified path data
  240. *
  241. * Arguments:
  242. *
  243. * [IN] points - Point to an array of path points
  244. * [IN] types - Specify path point types
  245. * count - Number of path points
  246. * fillMode - Path fill mode
  247. *
  248. * Return Value:
  249. *
  250. * NONE
  251. *
  252. \**************************************************************************/
  253. GpPath::GpPath(
  254. const GpPointF* points,
  255. const BYTE* types,
  256. INT count,
  257. GpFillMode fillMode
  258. )
  259. {
  260. SetValid(FALSE);
  261. // Validate function parameters
  262. if (count <= 0 ||
  263. (count > 0 && (!points || !types)) ||
  264. (fillMode != FillModeAlternate && fillMode != FillModeWinding))
  265. {
  266. WARNING(("Invalid path data in GpPath::GpPath"));
  267. return;
  268. }
  269. InitDefaultState(fillMode);
  270. // Validate path point types
  271. if (!ValidatePathTypes(types, count, &SubpathCount, &HasBezier))
  272. {
  273. WARNING(("Invalid path type information"));
  274. return;
  275. }
  276. // Copy path point and type information
  277. SetValid(Types.AddMultiple(types, count) == Ok &&
  278. Points.AddMultiple(points, count) == Ok);
  279. if(IsValid()) {
  280. // Make sure the first point is the start type.
  281. Types.First() = PathPointTypeStart;
  282. }
  283. }
  284. //--------------------------------
  285. // Constructor for polygon.
  286. //--------------------------------
  287. GpPath::GpPath(
  288. const GpPointF *points,
  289. INT count,
  290. GpPointF *stackPoints,
  291. BYTE *stackTypes,
  292. INT stackCount,
  293. GpFillMode fillMode,
  294. DpPathFlags flags
  295. ) : DpPath(points, count, stackPoints, stackTypes, stackCount,
  296. fillMode, flags)
  297. {
  298. InvalidateCache();
  299. }
  300. //--------------------------------
  301. // Copy constructor.
  302. //--------------------------------
  303. GpPath::GpPath(const GpPath* path) : DpPath(path)
  304. {
  305. SetValid(path != NULL);
  306. InvalidateCache();
  307. }
  308. /**************************************************************************\
  309. *
  310. * Function Description:
  311. *
  312. * Copies the path data. Points and Types array in pathData
  313. * must be allocated by the caller.
  314. *
  315. * Arguments:
  316. *
  317. * [OUT] pathData - the path data.
  318. *
  319. * Return Value:
  320. *
  321. * TRUE if successfull.
  322. *
  323. \**************************************************************************/
  324. GpStatus
  325. DpPath::GetPathData(GpPathData* pathData)
  326. {
  327. if ((!pathData) || (!pathData->Points) || (!pathData->Types) || (pathData->Count < 0))
  328. return InvalidParameter;
  329. INT count = GetPointCount();
  330. const GpPointF* points = GetPathPoints();
  331. const BYTE* types = GetPathTypes();
  332. if (pathData->Count >= count)
  333. {
  334. if (count > 0)
  335. {
  336. GpMemcpy(pathData->Points, points, count*sizeof(GpPointF));
  337. GpMemcpy(pathData->Types, types, count);
  338. }
  339. pathData->Count = count;
  340. return Ok;
  341. }
  342. else
  343. return OutOfMemory;
  344. }
  345. /**************************************************************************\
  346. *
  347. * Function Description:
  348. *
  349. * Set a marker at the current location. You cannot set a marker at the
  350. * first position.
  351. *
  352. * Arguments:
  353. *
  354. * None
  355. *
  356. * Return Value:
  357. *
  358. * Status
  359. *
  360. \**************************************************************************/
  361. GpStatus
  362. GpPath::SetMarker()
  363. {
  364. INT count = Types.GetCount();
  365. BYTE* types = Types.GetDataBuffer();
  366. // Don't set a marker at the first point.
  367. if(count > 1 && types)
  368. {
  369. types[count - 1] |= PathPointTypePathMarker;
  370. UpdateUid();
  371. }
  372. return Ok;
  373. }
  374. /**************************************************************************\
  375. *
  376. * Function Description:
  377. *
  378. * Clears all the markers in the path.
  379. *
  380. * Arguments:
  381. *
  382. * None
  383. *
  384. * Return Value:
  385. *
  386. * Status
  387. *
  388. \**************************************************************************/
  389. GpStatus
  390. GpPath::ClearMarkers()
  391. {
  392. INT count = Types.GetCount();
  393. BYTE* types = Types.GetDataBuffer();
  394. BOOL modified = FALSE;
  395. if(count > 0 && types)
  396. {
  397. for(INT i = 0; i < count; i++)
  398. {
  399. if(types[i] & PathPointTypePathMarker)
  400. {
  401. types[i] &= ~PathPointTypePathMarker;
  402. modified = TRUE;
  403. }
  404. }
  405. }
  406. if(modified)
  407. {
  408. UpdateUid();
  409. }
  410. return Ok;
  411. }
  412. /**************************************************************************\
  413. *
  414. * Function Description:
  415. *
  416. * Set the path data.
  417. *
  418. * Arguments:
  419. *
  420. * [IN] pathData - the path data.
  421. *
  422. * Return Value:
  423. *
  424. * TRUE if successfull.
  425. *
  426. \**************************************************************************/
  427. GpStatus
  428. DpPath::SetPathData(const GpPathData* pathData)
  429. {
  430. if(!pathData || pathData->Count <= 0)
  431. return InvalidParameter;
  432. INT count = pathData->Count;
  433. DpPathIterator iter(pathData->Points, pathData->Types, count);
  434. if(!iter.IsValid())
  435. return InvalidParameter;
  436. Points.Reset(FALSE);
  437. Types.Reset(FALSE);
  438. GpPointF* points = Points.AddMultiple(count);
  439. BYTE* types = Types.AddMultiple(count);
  440. if(points && types)
  441. {
  442. INT number, startIndex, endIndex;
  443. BOOL isClosed = FALSE;
  444. while(number = iter.NextSubpath(&startIndex, &endIndex, &isClosed))
  445. {
  446. GpMemcpy(
  447. points,
  448. pathData->Points + startIndex,
  449. number*sizeof(GpPointF)
  450. );
  451. GpMemcpy(
  452. types,
  453. pathData->Types + startIndex,
  454. number
  455. );
  456. points += number;
  457. types += number;
  458. }
  459. SetValid(TRUE);
  460. HasBezier = iter.HasCurve();
  461. Flags = PossiblyNonConvex;
  462. SubpathCount = iter.GetSubpathCount();
  463. IsSubpathActive = !isClosed;
  464. UpdateUid();
  465. return Ok;
  466. }
  467. else
  468. return OutOfMemory;
  469. }
  470. // Determine if the specified points form a rectangle that is axis aligned.
  471. // Do the test in the coordinate space specified by the matrix (if present).
  472. // Covert the REAL values to FIX4 values so that the test is compatible
  473. // with what the rasterizer would do.
  474. // If transformedBounds is not NULL, return the rect (if it is a rect)
  475. // in the transformed (device) space.
  476. BOOL
  477. IsRectanglePoints(
  478. const GpPointF* points,
  479. INT count,
  480. const GpMatrix * matrix,
  481. GpRectF * transformedBounds
  482. )
  483. {
  484. if(count < 4 || count > 5)
  485. return FALSE;
  486. GpPointF transformedPoints[5];
  487. if ((matrix != NULL) && (!matrix->IsIdentity()))
  488. {
  489. matrix->Transform(points, transformedPoints, count);
  490. points = transformedPoints;
  491. }
  492. PointFix4 fix4Points[5];
  493. fix4Points[0].Set(points[0].X, points[0].Y);
  494. if(count == 5)
  495. {
  496. fix4Points[4].Set(points[4].X, points[4].Y);
  497. if(fix4Points[0].X != fix4Points[4].X || fix4Points[0].Y != fix4Points[4].Y)
  498. return FALSE;
  499. }
  500. fix4Points[1].Set(points[1].X, points[1].Y);
  501. fix4Points[2].Set(points[2].X, points[2].Y);
  502. fix4Points[3].Set(points[3].X, points[3].Y);
  503. REAL maxValue;
  504. if (fix4Points[0].Y == fix4Points[1].Y)
  505. {
  506. if ((fix4Points[2].Y == fix4Points[3].Y) &&
  507. (fix4Points[0].X == fix4Points[3].X) &&
  508. (fix4Points[1].X == fix4Points[2].X))
  509. {
  510. if (transformedBounds != NULL)
  511. {
  512. transformedBounds->X = min(points[0].X, points[1].X);
  513. maxValue = max(points[0].X, points[1].X);
  514. transformedBounds->Width = maxValue - transformedBounds->X;
  515. transformedBounds->Y = min(points[0].Y, points[2].Y);
  516. maxValue = max(points[0].Y, points[2].Y);
  517. transformedBounds->Height = maxValue - transformedBounds->Y;
  518. }
  519. return TRUE;
  520. }
  521. }
  522. else if ((fix4Points[0].X == fix4Points[1].X) &&
  523. (fix4Points[2].X == fix4Points[3].X) &&
  524. (fix4Points[0].Y == fix4Points[3].Y) &&
  525. (fix4Points[1].Y == fix4Points[2].Y))
  526. {
  527. if (transformedBounds != NULL)
  528. {
  529. transformedBounds->X = min(points[0].X, points[2].X);
  530. maxValue = max(points[0].X, points[2].X);
  531. transformedBounds->Width = maxValue - transformedBounds->X;
  532. transformedBounds->Y = min(points[0].Y, points[1].Y);
  533. maxValue = max(points[0].Y, points[1].Y);
  534. transformedBounds->Height = maxValue - transformedBounds->Y;
  535. }
  536. return TRUE;
  537. }
  538. return FALSE;
  539. }
  540. BOOL
  541. GpPath::IsRectangle(
  542. const GpMatrix * matrix,
  543. GpRectF * transformedBounds
  544. ) const
  545. {
  546. if((SubpathCount != 1) || HasBezier)
  547. return FALSE;
  548. INT count = GetPointCount();
  549. GpPointF* points = Points.GetDataBuffer();
  550. return IsRectanglePoints(points, count, matrix, transformedBounds);
  551. }
  552. BOOL
  553. DpPath::IsRectangle(
  554. const GpMatrix * matrix,
  555. GpRectF * transformedBounds
  556. ) const
  557. {
  558. if ((GetSubpathCount() != 1) || HasCurve())
  559. return FALSE;
  560. INT count = GetPointCount();
  561. GpPointF* points = Points.GetDataBuffer();
  562. return IsRectanglePoints(points, count, matrix, transformedBounds);
  563. }
  564. /**************************************************************************\
  565. *
  566. * Function Description:
  567. *
  568. * Determine if the receiver and path represent the same path
  569. *
  570. * Arguments:
  571. *
  572. * [IN] path - GpPath to compare
  573. *
  574. * Return Value:
  575. *
  576. * TRUE if the paths are the same.
  577. *
  578. * Created - 5/27/99 peterost
  579. *
  580. \**************************************************************************/
  581. BOOL GpPath::IsEqual(const GpPath* path) const
  582. {
  583. if (path == this)
  584. return TRUE;
  585. INT count;
  586. if (IsValid() == path->IsValid() &&
  587. (count=GetPointCount()) == path->GetPointCount() &&
  588. HasBezier == path->HasBezier &&
  589. FillMode == path->FillMode &&
  590. Flags == path->Flags &&
  591. IsSubpathActive == path->IsSubpathActive &&
  592. SubpathCount == path->SubpathCount)
  593. {
  594. BYTE* types = path->Types.GetDataBuffer();
  595. BYTE* mytypes = Types.GetDataBuffer();
  596. GpPointF* points = path->Points.GetDataBuffer();
  597. GpPointF* mypoints = Points.GetDataBuffer();
  598. for (INT i=0; i<count; i++)
  599. {
  600. if (types[i] != mytypes[i] ||
  601. points[i].X != mypoints[i].X ||
  602. points[i].Y != mypoints[i].Y)
  603. {
  604. return FALSE;
  605. }
  606. }
  607. return TRUE;
  608. }
  609. else
  610. {
  611. return FALSE;
  612. }
  613. }
  614. VOID
  615. GpPath::InitDefaultState(
  616. GpFillMode fillMode
  617. )
  618. {
  619. DpPath::InitDefaultState(fillMode);
  620. InvalidateCache();
  621. }
  622. /**************************************************************************\
  623. *
  624. * Function Description:
  625. *
  626. * Validate path point type information
  627. *
  628. * Arguments:
  629. *
  630. * [IN] types - Point to an array of path point types
  631. * count - Number of points
  632. * subpathCount - Return the number of subpaths
  633. * hasBezier - Return whether the path has Bezier segments
  634. * [IN] needsFirstPointToBeStartPoint - TRUE if this data needs to start
  635. * with a StartPoint. (Default is TRUE)
  636. *
  637. * Return Value:
  638. *
  639. * TRUE if the path point type information is valid
  640. * FALSE otherwise
  641. *
  642. \**************************************************************************/
  643. BOOL
  644. DpPath::ValidatePathTypes(
  645. const BYTE* types,
  646. INT count,
  647. INT* subpathCount,
  648. BOOL* hasBezier
  649. )
  650. {
  651. DpPathTypeIterator iter(types, count);
  652. if(!iter.IsValid())
  653. {
  654. WARNING(("Invalid path type information"));
  655. return FALSE;
  656. }
  657. *subpathCount = iter.GetSubpathCount();
  658. *hasBezier = iter.HasCurve();
  659. return iter.IsValid();
  660. }
  661. /**************************************************************************\
  662. *
  663. * Function Description:
  664. *
  665. * Private helper function to add points to a path object
  666. *
  667. * Arguments:
  668. *
  669. * [IN] points - Specify the points to be added
  670. * count - Number of points to add
  671. *
  672. * Return Value:
  673. *
  674. * Point to location in the point type data buffer
  675. * that corresponds to the *SECOND* path point added.
  676. *
  677. * The first point type is always handled inside this
  678. * function:
  679. *
  680. * 1. If either the previous subpath is closed, or addClosedFigure
  681. * parameter is TRUE, the first point type will be StartPoint.
  682. *
  683. * 2. Otherwise, the previous subpath is open and addClosedFigure
  684. * parameter is FALSE. We have two separate cases to handle:
  685. *
  686. * 2.1 if the first point to be added is the same as the last
  687. * point of the open subpath, then the first point is ignored.
  688. *
  689. * 2.2 otherwise, the first point type will be LinePoint.
  690. *
  691. * NULL if there is an error. In this case, existing path
  692. * data is not affected.
  693. *
  694. * Note:
  695. *
  696. * We assume the caller has already obtained a lock
  697. * on the path object.
  698. *
  699. \**************************************************************************/
  700. BYTE*
  701. GpPath::AddPointHelper(
  702. const GpPointF* points,
  703. INT count,
  704. BOOL addClosedFigure
  705. )
  706. {
  707. // If we're adding a closed figure, then make sure
  708. // there is no more currently active subpath.
  709. if (addClosedFigure)
  710. StartFigure();
  711. INT origCount = GetPointCount();
  712. BOOL isDifferentPoint = TRUE;
  713. // Check if the first point is the same as the last point.
  714. if(IsSubpathActive && origCount > 0)
  715. {
  716. GpPointF lastPt = Points.Last();
  717. if ((REALABS(points->X - lastPt.X) < REAL_EPSILON) &&
  718. (REALABS(points->Y - lastPt.Y) < REAL_EPSILON) )
  719. {
  720. if(count == 1)
  721. return NULL;
  722. // case 2.1 above
  723. // Skip the first point and its type.
  724. count--;
  725. points++;
  726. isDifferentPoint = FALSE;
  727. }
  728. }
  729. // Resize Points and Types
  730. GpPointF* pointbuf = Points.AddMultiple(count);
  731. BYTE* typebuf = Types.AddMultiple(count);
  732. if(pointbuf == NULL || typebuf == NULL)
  733. {
  734. // Resize the original size.
  735. Points.SetCount(origCount);
  736. Types.SetCount(origCount);
  737. return NULL;
  738. }
  739. // Record the type of the first point (Start or Line Point).
  740. if (!IsSubpathActive)
  741. {
  742. // case 1 above
  743. *typebuf++ = PathPointTypeStart;
  744. SubpathCount++; // Starting a new subpath.
  745. }
  746. else
  747. {
  748. // If the first point is different, add a Line type.
  749. // Otherwise, skip the first point and its type.
  750. if(isDifferentPoint)
  751. {
  752. // case 2.2 above
  753. *typebuf++ = PathPointTypeLine;
  754. }
  755. }
  756. // Copy path point data
  757. GpMemcpy(pointbuf, points, count*sizeof(GpPointF));
  758. // Subpath is active if the added figure is not closed.
  759. if(!addClosedFigure)
  760. IsSubpathActive = TRUE;
  761. UpdateUid();
  762. InvalidateCache();
  763. // Return the starting location for the new point type data
  764. // From the second point type.
  765. return typebuf;
  766. }
  767. /**************************************************************************\
  768. *
  769. * Function Description:
  770. *
  771. * Add a series of line segments to the current path object
  772. *
  773. * Arguments:
  774. *
  775. * [IN] points - Specify the line points
  776. * count - Number of points
  777. *
  778. * Return Value:
  779. *
  780. * Status code
  781. *
  782. \**************************************************************************/
  783. GpStatus
  784. GpPath::AddLines(
  785. const GpPointF* points,
  786. INT count
  787. )
  788. {
  789. ASSERT(IsValid());
  790. // Validate function parameters
  791. if (points == NULL || count < 1)
  792. return InvalidParameter;
  793. InvalidateCache();
  794. // Call the internal helper function to add the points
  795. BYTE* types = AddPointHelper(points, count, FALSE);
  796. if (types == NULL)
  797. {
  798. if(count > 1)
  799. return OutOfMemory;
  800. else
  801. return Ok;
  802. }
  803. // Set path point type information
  804. GpMemset(types, PathPointTypeLine, count-1);
  805. // IsSubpathActive = TRUE; This is set in AddPointHelper. - ikkof
  806. UpdateUid();
  807. return Ok;
  808. }
  809. /**************************************************************************\
  810. *
  811. * Function Description:
  812. *
  813. * Add rectangles to the current path object
  814. *
  815. * Arguments:
  816. *
  817. * [IN] rects - Specify the rectangles to be added
  818. * count - Number of rectangles
  819. *
  820. * Return Value:
  821. *
  822. * Status code
  823. *
  824. \**************************************************************************/
  825. GpStatus
  826. GpPath::AddRects(
  827. const GpRectF* rect,
  828. INT count
  829. )
  830. {
  831. if (count < 1 || rect == NULL)
  832. return InvalidParameter;
  833. // NOTE: We don't need a lock here because
  834. // AddPolygon will handle it.
  835. // Add one rectangle at a time as a polygon
  836. GpPointF points[4];
  837. GpStatus status;
  838. for ( ; count--; rect++)
  839. {
  840. if (rect->IsEmptyArea())
  841. continue;
  842. // NOTE: Rectangle points are added in clockwise
  843. // order, starting from the top-left corner.
  844. points[0].X = rect->GetLeft(); // top-left
  845. points[0].Y = rect->GetTop();
  846. points[1].X = rect->GetRight(); // top-right
  847. points[1].Y = rect->GetTop();
  848. points[2].X = rect->GetRight(); // bottom-right
  849. points[2].Y = rect->GetBottom();
  850. points[3].X = rect->GetLeft(); // bottom-left
  851. points[3].Y = rect->GetBottom();
  852. if ((status = AddPolygon(points, 4)) != Ok)
  853. return status;
  854. }
  855. return Ok;
  856. }
  857. GpStatus
  858. GpPath::AddRects(
  859. const RECT* rects,
  860. INT count
  861. )
  862. {
  863. if ((count < 1) || (rects == NULL))
  864. {
  865. return InvalidParameter;
  866. }
  867. // NOTE: We don't need a lock here because
  868. // AddPolygon will handle it.
  869. // Add one rectangle at a time as a polygon
  870. GpPointF points[4];
  871. GpStatus status;
  872. for ( ; count--; rects++)
  873. {
  874. if ((rects->left >= rects->right) || (rects->top >= rects->bottom))
  875. {
  876. continue;
  877. }
  878. // NOTE: Rectangle points are added in clockwise
  879. // order, starting from the top-left corner.
  880. points[0].X = (REAL)rects->left; // top-left
  881. points[0].Y = (REAL)rects->top;
  882. points[1].X = (REAL)rects->right; // top-right
  883. points[1].Y = (REAL)rects->top;
  884. points[2].X = (REAL)rects->right; // bottom-right
  885. points[2].Y = (REAL)rects->bottom;
  886. points[3].X = (REAL)rects->left; // bottom-left
  887. points[3].Y = (REAL)rects->bottom;
  888. if ((status = AddPolygon(points, 4)) != Ok)
  889. {
  890. return status;
  891. }
  892. }
  893. return Ok;
  894. }
  895. /**************************************************************************\
  896. *
  897. * Function Description:
  898. *
  899. * Add a polygon to the current path object
  900. *
  901. * Arguments:
  902. *
  903. * [IN] Specify the polygon points
  904. * count - Number of points
  905. *
  906. * Return Value:
  907. *
  908. * Status code
  909. *
  910. \**************************************************************************/
  911. GpStatus
  912. GpPath::AddPolygon(
  913. const GpPointF* points,
  914. INT count
  915. )
  916. {
  917. ASSERT(IsValid());
  918. if (count < 3 || points == NULL)
  919. {
  920. return InvalidParameter;
  921. }
  922. // Check if the last point is the same as the first point.
  923. // If so, ignore it.
  924. if (count > 3 &&
  925. points[0].X == points[count-1].X &&
  926. points[0].Y == points[count-1].Y)
  927. {
  928. count--;
  929. }
  930. // Call the internal helper function to add the points
  931. BYTE* types = AddPointHelper(points, count, TRUE);
  932. InvalidateCache();
  933. if (types == NULL)
  934. {
  935. return OutOfMemory;
  936. }
  937. // Set path point type information
  938. GpMemset(types, PathPointTypeLine, count-2);
  939. types[count-2] = PathPointTypeLine | PathPointTypeCloseSubpath;
  940. UpdateUid();
  941. return Ok;
  942. }
  943. #define PI TOREAL(3.1415926535897932)
  944. #define HALF_PI TOREAL(1.5707963267948966)
  945. /**************************************************************************\
  946. *
  947. * Function Description:
  948. *
  949. * Convert an angle defined in a box with (width, height) to
  950. * an angle defined in a square box.
  951. * In other words, this shrink x- and y-coordinates by width and height,
  952. * and then calculates the new angle.
  953. *
  954. * Arguments:
  955. *
  956. * [IN/OUT] angle - the angle is given in degrees and return it in radian.
  957. * [IN] width - the width of the box.
  958. * [IN] height - the height of the box.
  959. *
  960. * Return Value:
  961. *
  962. * NONE
  963. *
  964. * History:
  965. *
  966. * 02/22/1999 ikkof
  967. * Created it.
  968. *
  969. \**************************************************************************/
  970. VOID
  971. NormalizeAngle(REAL* angle, REAL width, REAL height)
  972. {
  973. REAL a = *angle;
  974. // Set the angle between 0 and 360 degrees.
  975. a = GpModF(a, 360);
  976. if(a < 0 || a > 360)
  977. {
  978. // The input data may have been too large or loo small
  979. // to calculate the mode. In that case, set to 0.
  980. a = 0;
  981. }
  982. if(width != height)
  983. {
  984. INT plane = 1;
  985. REAL b = a;
  986. if(a <= 90)
  987. plane = 1;
  988. else if(a <= 180)
  989. {
  990. plane = 2;
  991. b = 180 - a;
  992. }
  993. else if(a <= 270)
  994. {
  995. plane = 3;
  996. b = a - 180;
  997. }
  998. else
  999. {
  1000. plane = 4;
  1001. b = 360 - a;
  1002. }
  1003. b = b*PI/180; // Convert to radian
  1004. // Get the normalized angle in the plane 1.
  1005. a = TOREAL( atan2(width*sin(b), height*cos(b)) );
  1006. // Adjust to the angle in one of 4 planes.
  1007. switch(plane)
  1008. {
  1009. case 1:
  1010. default:
  1011. break;
  1012. case 2:
  1013. a = PI - a;
  1014. break;
  1015. case 3:
  1016. a = PI + a;
  1017. break;
  1018. case 4:
  1019. a = 2*PI - a;
  1020. break;
  1021. }
  1022. }
  1023. else
  1024. {
  1025. a = a*PI/180; // Convert to radian.
  1026. }
  1027. *angle = a;
  1028. }
  1029. /**************************************************************************\
  1030. *
  1031. * Function Description:
  1032. *
  1033. * Convert the start and sweep angles defined in a box with (width, height)
  1034. * to an angle defined in a square box.
  1035. * In other words, this shrink x- and y-coordinates by width and height,
  1036. * and then calculates the new angles.
  1037. *
  1038. * Arguments:
  1039. *
  1040. * [IN/OUT] startAngle - it is given in degrees and return it in radian.
  1041. * [IN/OUT] sweepAngle - it is given in degrees and return it in radian.
  1042. * [IN] width - the width of the box.
  1043. * [IN] height - the height of the box.
  1044. *
  1045. * Return Value:
  1046. *
  1047. * INT - +1 if sweeping in clockwise and -1 in counterclockwise.
  1048. *
  1049. * History:
  1050. *
  1051. * 02/22/1999 ikkof
  1052. * Created it.
  1053. *
  1054. \**************************************************************************/
  1055. INT
  1056. NormalizeArcAngles(
  1057. REAL* startAngle,
  1058. REAL* sweepAngle,
  1059. REAL width,
  1060. REAL height
  1061. )
  1062. {
  1063. REAL a0 = *startAngle; // The start angle.
  1064. REAL dA = *sweepAngle;
  1065. REAL a1 = a0 + dA; // The end angle.
  1066. INT sweepSign;
  1067. if(dA > 0)
  1068. sweepSign = 1;
  1069. else
  1070. {
  1071. sweepSign = - 1;
  1072. dA = - dA; // Convert to a positive sweep angle.
  1073. }
  1074. // Normalize the start and end angle.
  1075. NormalizeAngle(&a0, width, height);
  1076. NormalizeAngle(&a1, width, height);
  1077. if(dA < 360)
  1078. {
  1079. if(sweepSign > 0)
  1080. {
  1081. dA = a1 - a0;
  1082. }
  1083. else
  1084. {
  1085. dA = a0 - a1;
  1086. }
  1087. if(dA < 0)
  1088. dA += 2*PI;
  1089. }
  1090. else
  1091. dA = 2*PI; // Don't sweep more than once.
  1092. *startAngle = a0;
  1093. *sweepAngle = dA;
  1094. return sweepSign;
  1095. }
  1096. /**************************************************************************\
  1097. *
  1098. * Function Description:
  1099. *
  1100. * Convert an elliptical arc to a series of Bezier curve segments
  1101. *
  1102. * Arguments:
  1103. *
  1104. * points - Specify a point buffer for returning Bezier control points
  1105. * The array should be able to hold 13 elements or more.
  1106. * rect - Specify the bounding box for the ellipse
  1107. * startAngle - Start angle (in elliptical space and degrees)
  1108. * sweepAngle - Sweep angle
  1109. * positive to sweep clockwise
  1110. * negative to sweep counterclockwise
  1111. *
  1112. * Return Value:
  1113. *
  1114. * Number of Bezier control points generated
  1115. * 0 if sweep angle is 0
  1116. * -1 if bounding rectangle is empty
  1117. *
  1118. \**************************************************************************/
  1119. INT
  1120. GpPath::GetArcPoints(
  1121. GpPointF* points,
  1122. const GpRectF& rect,
  1123. REAL startAngle,
  1124. REAL sweepAngle
  1125. )
  1126. {
  1127. if (rect.IsEmptyArea())
  1128. return -1;
  1129. else if (sweepAngle == 0)
  1130. return 0;
  1131. // Determine which direction we should sweep
  1132. // and clamp sweep angle to a max of 360 degrees
  1133. // Both start and sweep angles are conveted to radian.
  1134. INT sweepSign = NormalizeArcAngles(
  1135. &startAngle,
  1136. &sweepAngle,
  1137. rect.Width,
  1138. rect.Height);
  1139. // Temporary variables
  1140. REAL dx, dy;
  1141. REAL w2, h2;
  1142. w2 = rect.Width / 2;
  1143. h2 = rect.Height / 2;
  1144. dx = rect.X + w2;
  1145. dy = rect.Y + h2;
  1146. // Determine the number of Bezier segments needed
  1147. int segments, count;
  1148. GpMatrix m;
  1149. segments = (INT) (sweepAngle / HALF_PI);
  1150. if (segments*HALF_PI < sweepAngle)
  1151. segments++;
  1152. if (segments == 0)
  1153. segments = 1;
  1154. else if (segments > 4)
  1155. segments = 4;
  1156. count = segments*3 + 1;
  1157. while (segments--)
  1158. {
  1159. // Compute the Bezier control points in unit-circle space
  1160. REAL A, C, S;
  1161. REAL x, y;
  1162. A = (sweepAngle > HALF_PI) ? HALF_PI/2 : sweepAngle/2;
  1163. C = REALCOS(A);
  1164. S = REALSIN(A);
  1165. x = (4 - C) / 3;
  1166. y = (3 - C) * S / (3 + 3*C);
  1167. if (sweepSign > 0)
  1168. {
  1169. // clockwise sweep
  1170. points[0].X = C;
  1171. points[0].Y = -S;
  1172. points[1].X = x;
  1173. points[1].Y = -y;
  1174. points[2].X = x;
  1175. points[2].Y = y;
  1176. points[3].X = C;
  1177. points[3].Y = S;
  1178. }
  1179. else
  1180. {
  1181. // counterclockwise sweep
  1182. points[0].X = C;
  1183. points[0].Y = S;
  1184. points[1].X = x;
  1185. points[1].Y = y;
  1186. points[2].X = x;
  1187. points[2].Y = -y;
  1188. points[3].X = C;
  1189. points[3].Y = -S;
  1190. }
  1191. // Transform the control points to elliptical space
  1192. m.Reset();
  1193. m.Translate(dx, dy);
  1194. m.Scale(w2, h2);
  1195. REAL theta = (startAngle + sweepSign*A)*180/PI;
  1196. m.Rotate(theta); // Rotate takes degrees.
  1197. if(segments > 0)
  1198. m.Transform(points, 3);
  1199. else
  1200. m.Transform(points, 4); // Include the last point.
  1201. if(sweepSign > 0)
  1202. startAngle += HALF_PI;
  1203. else
  1204. startAngle -= HALF_PI;
  1205. sweepAngle -= HALF_PI;
  1206. points += 3;
  1207. }
  1208. return count;
  1209. }
  1210. /**************************************************************************\
  1211. *
  1212. * Function Description:
  1213. *
  1214. * Add an elliptical arc to the current path object
  1215. *
  1216. * Arguments:
  1217. *
  1218. * rect - Specify the bounding rectangle for the ellipse
  1219. * startAngle - Starting angle for the arc
  1220. * sweepAngle - Sweep angle for the arc
  1221. *
  1222. * Return Value:
  1223. *
  1224. * Status code
  1225. *
  1226. \**************************************************************************/
  1227. GpStatus
  1228. GpPath::AddArc(
  1229. const GpRectF& rect,
  1230. REAL startAngle,
  1231. REAL sweepAngle
  1232. )
  1233. {
  1234. GpPointF points[13];
  1235. INT count;
  1236. BOOL isClosed = FALSE;
  1237. if(sweepAngle >= 360)
  1238. {
  1239. sweepAngle = 360;
  1240. isClosed = TRUE;
  1241. }
  1242. else if(sweepAngle <= - 360)
  1243. {
  1244. sweepAngle = - 360;
  1245. isClosed = TRUE;
  1246. }
  1247. // Convert arc to Bezier curve segments
  1248. count = GetArcPoints(points, rect, startAngle, sweepAngle);
  1249. // Add resulting Bezier curve segment to the path
  1250. GpStatus status = Ok;
  1251. if(count > 0)
  1252. {
  1253. AddBeziers(points, count);
  1254. if(isClosed)
  1255. CloseFigure();
  1256. }
  1257. else if(count < 0)
  1258. status = InvalidParameter;
  1259. InvalidateCache();
  1260. return status;
  1261. }
  1262. /**************************************************************************\
  1263. *
  1264. * Function Description:
  1265. *
  1266. * Add an ellipse to the current path object
  1267. *
  1268. * Arguments:
  1269. *
  1270. * rect - Bounding rectangle for the ellipse
  1271. *
  1272. * Return Value:
  1273. *
  1274. * Status code
  1275. *
  1276. * History:
  1277. *
  1278. * 02/22/1999 ikkof
  1279. * Defined an array of a circle with radius 1 and used it.
  1280. *
  1281. \**************************************************************************/
  1282. GpStatus
  1283. GpPath::AddEllipse(
  1284. const GpRectF& rect
  1285. )
  1286. {
  1287. GpPointF points[13];
  1288. INT count = 13;
  1289. REAL u_cir = 4*(REALSQRT(2.0) - 1)/3;
  1290. GpPointF center;
  1291. REAL wHalf, hHalf;
  1292. wHalf = rect.Width/2;
  1293. hHalf = rect.Height/2;
  1294. center.X = rect.X + wHalf;
  1295. center.Y = rect.Y + hHalf;
  1296. // 4 Bezier segment of a circle with radius 1.
  1297. points[ 0].X = 1; points[ 0].Y = 0;
  1298. points[ 1].X = 1; points[ 1].Y = u_cir;
  1299. points[ 2].X = u_cir; points[ 2].Y = 1;
  1300. points[ 3].X = 0; points[ 3].Y = 1;
  1301. points[ 4].X = -u_cir; points[ 4].Y = 1;
  1302. points[ 5].X = -1; points[ 5].Y = u_cir;
  1303. points[ 6].X = -1; points[ 6].Y = 0;
  1304. points[ 7].X = -1; points[ 7].Y = -u_cir;
  1305. points[ 8].X = -u_cir; points[ 8].Y = -1;
  1306. points[ 9].X = 0; points[ 9].Y = -1;
  1307. points[10].X = u_cir; points[10].Y = -1;
  1308. points[11].X = 1; points[11].Y = -u_cir;
  1309. points[12].X = 1; points[12].Y = 0;
  1310. // Scale to the appropriate size.
  1311. for(INT i = 0; i < count; i++)
  1312. {
  1313. points[i].X = points[i].X*wHalf + center.X;
  1314. points[i].Y = points[i].Y*hHalf + center.Y;
  1315. }
  1316. // Add resulting Bezier curve segments to the path
  1317. GpStatus status;
  1318. StartFigure();
  1319. status = AddBeziers(points, count);
  1320. CloseFigure();
  1321. InvalidateCache();
  1322. UpdateUid();
  1323. return status;
  1324. }
  1325. /**************************************************************************\
  1326. *
  1327. * Function Description:
  1328. *
  1329. * Add an elliptical pie to the current path object
  1330. *
  1331. * Arguments:
  1332. *
  1333. * rect - Bounding rectangle for the ellipse
  1334. * startAngle - Specify the starting angle for the pie
  1335. * sweepAngle - Sweep angle for the pie
  1336. *
  1337. * Return Value:
  1338. *
  1339. * Status code
  1340. *
  1341. \**************************************************************************/
  1342. GpStatus
  1343. GpPath::AddPie(
  1344. const GpRectF& rect,
  1345. REAL startAngle,
  1346. REAL sweepAngle
  1347. )
  1348. {
  1349. GpPointF pt;
  1350. StartFigure();
  1351. // Add the center point.
  1352. pt.X = rect.X + rect.Width/2;
  1353. pt.Y = rect.Y + rect.Height/2;
  1354. GpStatus status = AddLines(&pt, 1);
  1355. // Add the arc points.
  1356. if(status == Ok)
  1357. status = AddArc(rect, startAngle, sweepAngle);
  1358. CloseFigure();
  1359. InvalidateCache();
  1360. UpdateUid();
  1361. return status;
  1362. }
  1363. /**************************************************************************\
  1364. *
  1365. * Function Description:
  1366. *
  1367. * Add Bezier curve segments to the current path object
  1368. *
  1369. * Arguments:
  1370. *
  1371. * [IN] points - Specify Bezier control points
  1372. * count - Number of points
  1373. *
  1374. * Return Value:
  1375. *
  1376. * Status code
  1377. *
  1378. \**************************************************************************/
  1379. GpStatus
  1380. GpPath::AddBeziers(
  1381. const GpPointF* points,
  1382. INT count
  1383. )
  1384. {
  1385. // Number of points must be 3 * N + 1
  1386. if ((!points) || (count < 4) || (count % 3 != 1))
  1387. {
  1388. return InvalidParameter;
  1389. }
  1390. // Check if the first point is the same as the last point.
  1391. INT firstType;
  1392. INT origCount = GetPointCount();
  1393. if(!IsSubpathActive)
  1394. {
  1395. SubpathCount++; // Starting a new subpath.
  1396. firstType = PathPointTypeStart;
  1397. }
  1398. else
  1399. {
  1400. if (origCount > 0)
  1401. {
  1402. GpPointF lastPt = Points.Last();
  1403. if ((REALABS(points[0].X - lastPt.X) < REAL_EPSILON) &&
  1404. (REALABS(points[0].Y - lastPt.Y) < REAL_EPSILON))
  1405. {
  1406. firstType = -1; // Indicating no copying of the first point.
  1407. points++;
  1408. count--;
  1409. }
  1410. else
  1411. {
  1412. firstType = PathPointTypeLine;
  1413. }
  1414. }
  1415. else
  1416. {
  1417. SubpathCount++;
  1418. firstType = PathPointTypeStart;
  1419. }
  1420. }
  1421. // Resize Points and Types
  1422. GpPointF* pointBuf = Points.AddMultiple(count);
  1423. BYTE* typeBuf = Types.AddMultiple(count);
  1424. if(pointBuf == NULL || typeBuf == NULL)
  1425. {
  1426. // Resize the original size.
  1427. Points.SetCount(origCount);
  1428. Types.SetCount(origCount);
  1429. return OutOfMemory;
  1430. }
  1431. GpMemcpy(pointBuf, points, count * sizeof(GpPointF));
  1432. GpMemset(typeBuf, PathPointTypeBezier, count);
  1433. if(firstType == PathPointTypeStart)
  1434. typeBuf[0] = PathPointTypeStart;
  1435. else if(firstType == PathPointTypeLine)
  1436. typeBuf[0] = PathPointTypeLine;
  1437. IsSubpathActive = TRUE;
  1438. HasBezier = TRUE;
  1439. InvalidateCache();
  1440. UpdateUid();
  1441. return Ok;
  1442. }
  1443. GpStatus
  1444. GpPath::AddBezier(
  1445. const GpPointF& pt1,
  1446. const GpPointF& pt2,
  1447. const GpPointF& pt3,
  1448. const GpPointF& pt4
  1449. )
  1450. {
  1451. GpPointF points[4];
  1452. points[0] = pt1;
  1453. points[1] = pt2;
  1454. points[2] = pt3;
  1455. points[3] = pt4;
  1456. return AddBeziers(points, 4);
  1457. }
  1458. GpStatus
  1459. GpPath::AddBezier(
  1460. REAL x1, REAL y1,
  1461. REAL x2, REAL y2,
  1462. REAL x3, REAL y3,
  1463. REAL x4, REAL y4
  1464. )
  1465. {
  1466. GpPointF points[4];
  1467. points[0].X = x1;
  1468. points[0].Y = y1;
  1469. points[1].X = x2;
  1470. points[1].Y = y2;
  1471. points[2].X = x3;
  1472. points[2].Y = y3;
  1473. points[3].X = x4;
  1474. points[3].Y = y4;
  1475. return AddBeziers(points, 4);
  1476. }
  1477. /**************************************************************************\
  1478. *
  1479. * Function Description:
  1480. *
  1481. * Add a path to the current path object.
  1482. * When connect is TRUE, this combine the end point of the current
  1483. * path and the start point of the given path if both paths are
  1484. * open.
  1485. * If either path is closed, the two paths will not be connected
  1486. * even if connect is set to TRUE.
  1487. *
  1488. * Arguments:
  1489. *
  1490. * [IN] points - Specify a subpath points
  1491. * [IN] types - Specify a subpath control types.
  1492. * [IN] count - Number of points
  1493. * [IN] connect - TRUE if two open paths needs to be connected.
  1494. *
  1495. * Return Value:
  1496. *
  1497. * Status code
  1498. *
  1499. * 02/09/2000 ikkof
  1500. * Created it.
  1501. *
  1502. \**************************************************************************/
  1503. GpStatus
  1504. GpPath::AddPath(
  1505. const GpPointF* points,
  1506. const BYTE* types,
  1507. INT count,
  1508. BOOL connect
  1509. )
  1510. {
  1511. GpStatus status = Ok;
  1512. if(points == NULL || types == NULL || count <= 0)
  1513. {
  1514. return InvalidParameter;
  1515. }
  1516. INT count1 = GetPointCount();
  1517. INT count2 = count;
  1518. const GpPointF* points2 = points;
  1519. const BYTE* types2 = types;
  1520. INT totalCount = count1 + count2;
  1521. BOOL forward1 = TRUE, forward2 = TRUE;
  1522. status = Points.ReserveSpace(count2);
  1523. if(status != Ok)
  1524. {
  1525. return status;
  1526. }
  1527. status = Types.ReserveSpace(count2);
  1528. if(status != Ok)
  1529. {
  1530. return status;
  1531. }
  1532. GpPointF* outPoints = Points.GetDataBuffer();
  1533. BYTE* outTypes = Types.GetDataBuffer();
  1534. const GpPointF* points1 = outPoints;
  1535. const BYTE* types1 = outTypes;
  1536. totalCount = CombinePaths(
  1537. totalCount,
  1538. outPoints,
  1539. outTypes,
  1540. count1,
  1541. points1,
  1542. types1,
  1543. forward1,
  1544. count2,
  1545. points2,
  1546. types2,
  1547. forward2,
  1548. connect
  1549. );
  1550. if( (totalCount >= count1) &&
  1551. ValidatePathTypes(outTypes, totalCount, &SubpathCount, &HasBezier))
  1552. {
  1553. count2 = totalCount - count1;
  1554. Points.AdjustCount(count2);
  1555. Types.AdjustCount(count2);
  1556. // Turn on the active subpath so that we can connect lines to this
  1557. // path.
  1558. // In order to not connect, call CloseFigure after adding.
  1559. IsSubpathActive = !IsClosedType(Types[Types.GetCount()-1]);
  1560. InvalidateCache();
  1561. UpdateUid();
  1562. return Ok;
  1563. }
  1564. else
  1565. {
  1566. return InvalidParameter;
  1567. }
  1568. }
  1569. GpStatus
  1570. GpPath::AddPath(const GpPath* path, BOOL connect)
  1571. {
  1572. if(!path)
  1573. {
  1574. return InvalidParameter;
  1575. }
  1576. INT count2 = path->GetPointCount();
  1577. const GpPointF* points2 = path->GetPathPoints();
  1578. const BYTE* types2 = path->GetPathTypes();
  1579. return AddPath(points2, types2, count2, connect);
  1580. }
  1581. /**************************************************************************\
  1582. *
  1583. * Function Description:
  1584. *
  1585. * Reverse the direction of the path.
  1586. *
  1587. * Arguments:
  1588. *
  1589. * NONE
  1590. *
  1591. * Return Value:
  1592. *
  1593. * Status code
  1594. *
  1595. * 02/09/2000 ikkof
  1596. * Created it.
  1597. *
  1598. \**************************************************************************/
  1599. GpStatus
  1600. GpPath::Reverse()
  1601. {
  1602. if(!IsValid())
  1603. return InvalidParameter;
  1604. INT count = GetPointCount();
  1605. GpPointF* points = Points.GetDataBuffer();
  1606. BYTE* types = Types.GetDataBuffer();
  1607. GpStatus status = Ok;
  1608. if(count > 1)
  1609. status = ::ReversePath(count, points, types);
  1610. UpdateUid();
  1611. return status;
  1612. }
  1613. GpStatus
  1614. GpPath::GetLastPoint(GpPointF* lastPoint)
  1615. {
  1616. INT count = GetPointCount();
  1617. if(count <= 0 || lastPoint == NULL)
  1618. return InvalidParameter;
  1619. GpPointF* points = Points.GetDataBuffer();
  1620. // Return the last point.
  1621. *lastPoint = points[count - 1];
  1622. return Ok;
  1623. }
  1624. GpPath*
  1625. GpPath::GetOpenPath()
  1626. {
  1627. BOOL openPath = TRUE;
  1628. return GetOpenOrClosedPath(openPath);
  1629. }
  1630. GpPath*
  1631. GpPath::GetClosedPath()
  1632. {
  1633. BOOL openPath = FALSE;
  1634. return GetOpenOrClosedPath(openPath);
  1635. }
  1636. GpPath*
  1637. GpPath::GetOpenOrClosedPath(BOOL openPath)
  1638. {
  1639. INT startIndex, endIndex;
  1640. BOOL isClosed;
  1641. const GpPointF* points = Points.GetDataBuffer();
  1642. const BYTE* types = Types.GetDataBuffer();
  1643. DpPathIterator iter(points, types, GetPointCount());
  1644. GpPath* path = new GpPath(FillMode);
  1645. if(path)
  1646. {
  1647. INT segmentCount = 0;
  1648. while(iter.NextSubpath(&startIndex, &endIndex, &isClosed))
  1649. {
  1650. if(isClosed != openPath)
  1651. {
  1652. // path->AddSubpath(points + startIndex, types + startIndex,
  1653. // endIndex - startIndex + 1);
  1654. BOOL connect = FALSE;
  1655. path->AddPath(points + startIndex, types + startIndex,
  1656. endIndex - startIndex + 1, connect);
  1657. segmentCount++;
  1658. }
  1659. }
  1660. if(segmentCount == 0)
  1661. {
  1662. delete path;
  1663. path = NULL;
  1664. }
  1665. }
  1666. return path;
  1667. }
  1668. /**************************************************************************\
  1669. *
  1670. * Function Description:
  1671. *
  1672. * Add an open cardinal spline curve to the current path object
  1673. *
  1674. * Arguments:
  1675. *
  1676. * [IN] points - Specify the spline points
  1677. * count - Number of points
  1678. * tension - Tension parameter
  1679. * offset - Index of the first point we're interested in
  1680. * numberOfSegments - Number of curve segments
  1681. *
  1682. * Return Value:
  1683. *
  1684. * Status code
  1685. *
  1686. \**************************************************************************/
  1687. #define DEFAULT_TENSION 0.5
  1688. GpStatus
  1689. GpPath::AddCurve(
  1690. const GpPointF* points,
  1691. INT count,
  1692. REAL tension,
  1693. INT offset,
  1694. INT numberOfSegments
  1695. )
  1696. {
  1697. // Verify input parameters
  1698. if (points == NULL ||
  1699. count < 2 ||
  1700. offset < 0 ||
  1701. offset >= count ||
  1702. numberOfSegments < 1 ||
  1703. numberOfSegments >= count-offset)
  1704. {
  1705. return InvalidParameter;
  1706. }
  1707. // Convert spline points to Bezier control points
  1708. GpPointF* bezierPoints;
  1709. INT bezierCount;
  1710. bezierPoints = ConvertSplineToBezierPoints(
  1711. points,
  1712. count,
  1713. offset,
  1714. numberOfSegments,
  1715. tension,
  1716. &bezierCount);
  1717. if (bezierPoints == NULL)
  1718. return OutOfMemory;
  1719. // Add the resulting Bezier segments to the current path
  1720. GpStatus status;
  1721. status = AddBeziers(bezierPoints, bezierCount);
  1722. delete[] bezierPoints;
  1723. return status;
  1724. }
  1725. GpStatus
  1726. GpPath::AddCurve(
  1727. const GpPointF* points,
  1728. INT count
  1729. )
  1730. {
  1731. return AddCurve(points,
  1732. count,
  1733. DEFAULT_TENSION,
  1734. 0,
  1735. count-1);
  1736. }
  1737. /**************************************************************************\
  1738. *
  1739. * Function Description:
  1740. *
  1741. * Add a closed cardinal spline curve to the current path object
  1742. *
  1743. * Arguments:
  1744. *
  1745. * [IN] points - Specify the spline points
  1746. * count - Number of points
  1747. * tension - Tension parameter
  1748. *
  1749. * Return Value:
  1750. *
  1751. * Status code
  1752. *
  1753. \**************************************************************************/
  1754. GpStatus
  1755. GpPath::AddClosedCurve(
  1756. const GpPointF* points,
  1757. INT count,
  1758. REAL tension
  1759. )
  1760. {
  1761. // Verify input parameters
  1762. if (points == NULL || count <= 2)
  1763. return InvalidParameter;
  1764. // Convert spline points to Bezier control points
  1765. GpPointF* bezierPoints;
  1766. INT bezierCount;
  1767. bezierPoints = ConvertSplineToBezierPoints(
  1768. points,
  1769. count,
  1770. 0,
  1771. count,
  1772. tension,
  1773. &bezierCount);
  1774. if (bezierPoints == NULL)
  1775. return OutOfMemory;
  1776. // Add the resulting Bezier segments as a closed curve
  1777. GpStatus status;
  1778. StartFigure();
  1779. status = AddBeziers(bezierPoints, bezierCount);
  1780. CloseFigure();
  1781. delete[] bezierPoints;
  1782. InvalidateCache();
  1783. UpdateUid();
  1784. return status;
  1785. }
  1786. GpStatus
  1787. GpPath::AddClosedCurve(
  1788. const GpPointF* points,
  1789. INT count
  1790. )
  1791. {
  1792. return AddClosedCurve(points, count, DEFAULT_TENSION);
  1793. }
  1794. /**************************************************************************\
  1795. *
  1796. * Function Description:
  1797. *
  1798. * Convert cardinal spline curve points to Bezier curve control points
  1799. *
  1800. * Arguments:
  1801. *
  1802. * [IN] points - Array of spline curve points
  1803. * count - Number of points in the "points" array
  1804. * offset - Specify the index of the first control point in
  1805. * the "points" array that the curve should start from
  1806. * numberOfSegments - Specify the number of curve segments to draw
  1807. * tension - Specify the tension parameter
  1808. * bezierCount - Return the number of Bezier control points
  1809. *
  1810. * Return Value:
  1811. *
  1812. * Pointer to an array of Bezier control points
  1813. * NULL if there is an error
  1814. *
  1815. * Reference:
  1816. *
  1817. * Spline Tutorial Notes
  1818. * Technical Memo No. 77
  1819. * Alvy Ray Smith
  1820. * Presented as tutorial notes at the 1983 SIGGRAPH, July 1983
  1821. * and the SIGGRAPH, July 1984
  1822. *
  1823. * Notes:
  1824. *
  1825. * Support for cardinal spline curves
  1826. *
  1827. * Cardinal splines are local interpolating splines, i.e. they
  1828. * pass through their control points and they maintain
  1829. * first-order continuity at their control points.
  1830. *
  1831. * a cardinal spline is specified by three parameters:
  1832. * a set of control points P1, ..., Pn
  1833. * tension parameter a
  1834. * close flag
  1835. *
  1836. * If n is 1, then the spline degenerates into a single point P1.
  1837. * If n > 1 and the close flag is false, the spline consists of
  1838. * n-1 cubic curve segments. The first curve segment starts from
  1839. * P1 and ends at P2. The last segment starts at Pn-1 and ends at Pn.
  1840. *
  1841. * The cubic curve segment from Pi to Pi+1 is determined by
  1842. * 4 control points:
  1843. * Pi-1 = (xi-1, yi-1)
  1844. * Pi = (xi, yi)
  1845. * Pi+1 = (xi+1, yi+1)
  1846. * Pi+2 = (xi+2, yi+2)
  1847. *
  1848. * The parametric equation is defined as:
  1849. *
  1850. * [ X(t) Y(t) ] = [t^3 t^2 t 1] * M * [ xi-1 yi-1 ]
  1851. * [ xi yi ]
  1852. * [ xi+1 yi+1 ]
  1853. * [ xi+2 yi+2 ]
  1854. *
  1855. * where t ranges from 0 to 1 and M is a 4x4 matrix satisfying
  1856. * the following constraints:
  1857. *
  1858. * X(0) = xi interpolating through control points
  1859. * X(1) = xi+1
  1860. * X'(0) = a(xi+1 - xi-1) first-order continuity
  1861. * X'(1) = a(xi+2 - xi)
  1862. *
  1863. * In the case of segments from P1 to P2 and from Pn-1 to Pn,
  1864. * we replicate the first and last control points, i.e. we
  1865. * define P0 = P1 and Pn+1 = Pn.
  1866. *
  1867. * If the close flag is true, we have an additional curve segment
  1868. * from Pn to Pn+1 = P1. For the segments near the beginning and
  1869. * the end of the spline, we wrap around the control points, i.e.
  1870. * P0 = Pn, Pn+1 = P1, and Pn+2 = P2.
  1871. *
  1872. \**************************************************************************/
  1873. GpPointF*
  1874. GpPath::ConvertSplineToBezierPoints(
  1875. const GpPointF* points,
  1876. INT count,
  1877. INT offset,
  1878. INT numberOfSegments,
  1879. REAL tension,
  1880. INT* bezierCount
  1881. )
  1882. {
  1883. BOOL closed;
  1884. GpPointF* bezierPoints;
  1885. ASSERT(count > 1 &&
  1886. offset >= 0 &&
  1887. offset < count &&
  1888. numberOfSegments > 0 &&
  1889. numberOfSegments <= count-offset);
  1890. // Curve is closed if the number of segments is equal to
  1891. // the number of curve points
  1892. closed = (numberOfSegments == count);
  1893. // Allocate memory to hold Bezier control points
  1894. *bezierCount = numberOfSegments*3 + 1;
  1895. bezierPoints = new GpPointF[*bezierCount];
  1896. if (bezierPoints == NULL)
  1897. return NULL;
  1898. // Convert each spline segment to a Bezier segment
  1899. // resulting in 3 additional Bezier points
  1900. GpPointF buffer[4], *q;
  1901. const GpPointF* p;
  1902. REAL a3;
  1903. a3 = tension / 3;
  1904. q = bezierPoints;
  1905. *q = points[offset];
  1906. for (INT index=offset; index < offset+numberOfSegments; index++)
  1907. {
  1908. if (index > 1 && index < count-2)
  1909. p = points + (index-1);
  1910. else
  1911. {
  1912. // Points near the beginning and end of the curve
  1913. // require special attention
  1914. if (closed)
  1915. {
  1916. // If the curve is closed, make sure the control points
  1917. // wrap around the beginning and end of the array.
  1918. buffer[0] = points[(index-1+count) % count];
  1919. buffer[1] = points[index];
  1920. buffer[2] = points[(index+1) % count];
  1921. buffer[3] = points[(index+2) % count];
  1922. }
  1923. else
  1924. {
  1925. // If the curve is not closed, replicate the first
  1926. // and last point in the array.
  1927. buffer[0] = points[(index > 0) ? (index-1) : 0];
  1928. buffer[1] = points[index];
  1929. buffer[2] = points[(index+1 < count) ? (index+1) : (count-1)];
  1930. buffer[3] = points[(index+2 < count) ? (index+2) : (count-1)];
  1931. }
  1932. p = buffer;
  1933. }
  1934. q[1].X = -a3*p[0].X + p[1].X + a3*p[2].X;
  1935. q[1].Y = -a3*p[0].Y + p[1].Y + a3*p[2].Y;
  1936. q[2].X = a3*p[1].X + p[2].X - a3*p[3].X;
  1937. q[2].Y = a3*p[1].Y + p[2].Y - a3*p[3].Y;
  1938. q[3] = p[2];
  1939. q += 3;
  1940. }
  1941. return bezierPoints;
  1942. }
  1943. /**************************************************************************\
  1944. *
  1945. * Function Description:
  1946. *
  1947. * Transform all path points by the specified matrix
  1948. *
  1949. * Arguments:
  1950. *
  1951. * matrix - Transform matrix
  1952. *
  1953. * Return Value:
  1954. *
  1955. * NONE
  1956. *
  1957. * Created:
  1958. *
  1959. * 02/08/1999 ikkof
  1960. * Created it.
  1961. *
  1962. \**************************************************************************/
  1963. VOID
  1964. GpPath::Transform(
  1965. const GpMatrix *matrix
  1966. )
  1967. {
  1968. ASSERT(IsValid());
  1969. if(matrix)
  1970. {
  1971. INT count = GetPointCount();
  1972. GpPointF* points = Points.GetDataBuffer();
  1973. matrix->Transform(points, count);
  1974. UpdateUid();
  1975. InvalidateCache();
  1976. }
  1977. }
  1978. // Debug only.
  1979. #if DBG
  1980. void DpPath::DisplayPath() {
  1981. INT size = GetPointCount();
  1982. const GpPointF *points = GetPathPoints();
  1983. const BYTE *types = GetPathTypes();
  1984. for(int i=0; i<size; i++)
  1985. {
  1986. WARNING(("points[%d].X = %ff;", i, points[i].X));
  1987. WARNING(("points[%d].Y = %ff;", i, points[i].Y));
  1988. WARNING(("types[%d] = 0x%x;", i, types[i]));
  1989. }
  1990. }
  1991. #endif
  1992. /**************************************************************************\
  1993. *
  1994. * Function Description:
  1995. *
  1996. * Callback to accumulate the flattened edges that the Rasterizer emits.
  1997. *
  1998. * Arguments:
  1999. *
  2000. * VOID *context, // pointer to the resulting path.
  2001. * POINT *pointArray, // Points to a 28.4 array of size 'vertexCount'
  2002. * INT vertexCount, // number of points to add.
  2003. * BOOL lastSubpath // The last point in this array is the last point
  2004. * // in a closed subpath.
  2005. *
  2006. * Return Value:
  2007. * BOOL
  2008. *
  2009. * History:
  2010. *
  2011. * 10/25/2000 asecchia & ericvan
  2012. * Created it.
  2013. *
  2014. \**************************************************************************/
  2015. BOOL PathFlatteningCallback(
  2016. VOID *context,
  2017. POINT *pointArray, // Points to a 28.4 array of size 'vertexCount'
  2018. // Note that we may modify the contents!
  2019. INT vertexCount,
  2020. PathEnumerateTermination lastSubpath // Last point in the subpath.
  2021. )
  2022. {
  2023. GpPath *result = static_cast<GpPath*>(context);
  2024. GpPointF *pointcast = (GpPointF*)(pointArray);
  2025. INT count = vertexCount;
  2026. // Don't add the last point if it's closed because the rasterizer
  2027. // emitted it twice - once for the first point.
  2028. if(lastSubpath == PathEnumerateCloseSubpath)
  2029. {
  2030. count--;
  2031. }
  2032. for(INT i = 0; i < count; i++)
  2033. {
  2034. // Convert the point array to real in place.
  2035. pointcast[i].X = FIX4TOREAL(pointArray[i].x);
  2036. pointcast[i].Y = FIX4TOREAL(pointArray[i].y);
  2037. }
  2038. // Add all the edges to the path as lines.
  2039. GpStatus status = result->AddLines(pointcast, count);
  2040. if(status == Ok)
  2041. {
  2042. if(lastSubpath == PathEnumerateCloseSubpath)
  2043. {
  2044. // last point in a subpath - close it.
  2045. status = result->CloseFigure();
  2046. }
  2047. if(lastSubpath == PathEnumerateEndSubpath)
  2048. {
  2049. // last point in an open subpath.
  2050. result->StartFigure();
  2051. }
  2052. }
  2053. return (status == Ok);
  2054. }
  2055. /**************************************************************************\
  2056. *
  2057. * Function Description:
  2058. *
  2059. * Flatten the path. The input matrix is the world to device transform.
  2060. * Our rasterizer will flatten at about 2/3 of a device pixel. In order
  2061. * to handle different flatness tolerances, we scale the path proportionally
  2062. * and pretend that it is bigger when we give it to the rasterizer for
  2063. * flattening. It flattens at 2/3 of a device pixel in this mocked up
  2064. * device space and we undo the flatness scale effectively redefining what
  2065. * size a device pixel is. This allows us to flatten to an arbitrary
  2066. * flatness tolerance.
  2067. *
  2068. * Arguments:
  2069. *
  2070. * [IN] matrix - Specifies the transform
  2071. * When matrix is NULL, the identity matrix is used.
  2072. * [IN] flatness - the flattening tolerance
  2073. *
  2074. * Return Value:
  2075. *
  2076. * Status
  2077. *
  2078. * Created:
  2079. *
  2080. * 10/25/2000 asecchia & ericvan
  2081. * Created it.
  2082. *
  2083. \**************************************************************************/
  2084. GpStatus
  2085. GpPath::Flatten(
  2086. DynByteArray *flattenTypes,
  2087. DynPointFArray *flattenPoints,
  2088. const GpMatrix *matrix,
  2089. const REAL flatness
  2090. ) const
  2091. {
  2092. // Enumerate the path.
  2093. FIXEDPOINTPATHENUMERATEFUNCTION pathEnumerationFunction = PathFlatteningCallback;
  2094. GpPath result;
  2095. // Calculate the scale - multiply by 16 for the rasterizer's 28.4 fixed
  2096. // point math. This 16x transform is implicitly reversed as we accumulate
  2097. // the points from the rasterizer in our callback function
  2098. REAL deviceFlatness = flatness/FlatnessDefault;
  2099. REAL flatnessInv = (16.0f)/deviceFlatness;
  2100. GpMatrix transform;
  2101. if(matrix)
  2102. {
  2103. transform = *matrix;
  2104. }
  2105. // apply the flatness transform so that we rasterize at an appropriately
  2106. // scaled resolution. We undo this part of the transform (not the 16x)
  2107. // at the end of this function.
  2108. transform.AppendScale(flatnessInv, flatnessInv);
  2109. // Do it.
  2110. if (!FixedPointPathEnumerate(
  2111. this,
  2112. &transform,
  2113. NULL,
  2114. PathEnumerateTypeFlatten, // don't automatically close open subpaths.
  2115. pathEnumerationFunction,
  2116. &result
  2117. ))
  2118. {
  2119. return(OutOfMemory);
  2120. }
  2121. // undo the implicit flatness transform.
  2122. transform.Reset();
  2123. transform.Scale(deviceFlatness, deviceFlatness);
  2124. result.Transform(&transform);
  2125. // Copy the points over. We should be using a detach on the DynArrays
  2126. // because we're throwing the temporary one away.
  2127. flattenPoints->Reset(FALSE);
  2128. flattenTypes->Reset(FALSE);
  2129. flattenPoints->AddMultiple(result.GetPathPoints(), result.GetPointCount());
  2130. flattenTypes->AddMultiple(result.GetPathTypes(), result.GetPointCount());
  2131. return Ok;
  2132. }
  2133. GpStatus
  2134. GpPath::Flatten(
  2135. const GpMatrix *matrix,
  2136. const REAL flatness
  2137. )
  2138. {
  2139. GpStatus status = Ok;
  2140. // Only flatten if it has beziers.
  2141. if(HasBezier)
  2142. {
  2143. DynPointFArray flattenPoints;
  2144. DynByteArray flattenTypes;
  2145. status = Flatten(
  2146. &flattenTypes,
  2147. &flattenPoints,
  2148. matrix,
  2149. flatness
  2150. );
  2151. if(status==Ok)
  2152. {
  2153. // Copy the points over. We should be using a detach on the DynArrays
  2154. // because we're throwing the temporary one away.
  2155. Points.ReplaceWith(&flattenPoints);
  2156. Types.ReplaceWith(&flattenTypes);
  2157. // Update to reflect the changed state of the path.
  2158. HasBezier = FALSE;
  2159. InvalidateCache();
  2160. UpdateUid();
  2161. }
  2162. }
  2163. else
  2164. {
  2165. // Flatten transforms the path even if it's already flat.
  2166. // Note: Transform(NULL) <=> Identity transform which is a NOP.
  2167. Transform(matrix);
  2168. }
  2169. return status;
  2170. }
  2171. /**************************************************************************\
  2172. *
  2173. * Function Description:
  2174. *
  2175. * Warp and flattens the control points and stores
  2176. * the results to the arrays of the flatten points.
  2177. *
  2178. * Arguments:
  2179. *
  2180. * [IN] matrix - Specifies the transform
  2181. * [IN] destPoint - The destination quad.
  2182. * [IN] count - the number of the quad points (3 or 4).
  2183. * [IN] srcRect - the original rectangle to warp.
  2184. * [IN] warpMode - Perspective or Bilinear (default is Bilinear).
  2185. *
  2186. * Return Value:
  2187. *
  2188. * Status
  2189. *
  2190. * Created:
  2191. *
  2192. * 11/10/1999 ikkof
  2193. * Created it.
  2194. *
  2195. \**************************************************************************/
  2196. GpStatus
  2197. GpPath::WarpAndFlatten(
  2198. DynByteArray* flattenTypes,
  2199. DynPointFArray* flattenPoints,
  2200. const GpMatrix* matrix,
  2201. const GpPointF* destPoint,
  2202. INT count,
  2203. const GpRectF& srcRect,
  2204. WarpMode warpMode
  2205. )
  2206. {
  2207. GpXPath xpath(this, srcRect, destPoint, count, warpMode);
  2208. return xpath.Flatten(flattenTypes, flattenPoints, matrix);
  2209. }
  2210. /**************************************************************************\
  2211. *
  2212. * Function Description:
  2213. *
  2214. * Warps and flattens the control points and transform itself to
  2215. * the flatten path.
  2216. *
  2217. * Arguments:
  2218. *
  2219. * [IN] matrix - Specifies the transform
  2220. * The identity matrix is used when matrix is NULL.
  2221. * [IN] destPoint - The destination quad.
  2222. * [IN] count - the number of the quad points (3 or 4).
  2223. * [IN] srcRect - the original rectangle to warp.
  2224. * [IN] warpMode - Perspective or Bilinear (default is Bilinear).
  2225. *
  2226. * Return Value:
  2227. *
  2228. * Status
  2229. *
  2230. * Created:
  2231. *
  2232. * 11/10/1999 ikkof
  2233. * Created it.
  2234. *
  2235. \**************************************************************************/
  2236. GpStatus
  2237. GpPath::WarpAndFlattenSelf(
  2238. GpMatrix* matrix,
  2239. const GpPointF* destPoint,
  2240. INT count,
  2241. const GpRectF& srcRect,
  2242. WarpMode warpMode
  2243. )
  2244. {
  2245. GpMatrix identity; // Identity matrix
  2246. GpXPath xpath(this, srcRect, destPoint, count, warpMode);
  2247. const INT buffSize = 32;
  2248. BYTE typesBuffer[buffSize];
  2249. GpPointF pointsBuffer[buffSize];
  2250. DynByteArray flattenTypes(&typesBuffer[0], buffSize);
  2251. DynPointFArray flattenPoints(&pointsBuffer[0], buffSize);
  2252. if(matrix == NULL)
  2253. matrix = &identity; // Use the identity matrix
  2254. GpStatus status = xpath.Flatten(&flattenTypes, &flattenPoints, matrix);
  2255. if(status == Ok)
  2256. {
  2257. INT flattenCount = flattenPoints.GetCount();
  2258. Points.Reset(FALSE);
  2259. Types.Reset(FALSE);
  2260. Points.AddMultiple(flattenPoints.GetDataBuffer(), flattenCount);
  2261. Types.AddMultiple(flattenTypes.GetDataBuffer(), flattenCount);
  2262. HasBezier = FALSE;
  2263. UpdateUid();
  2264. InvalidateCache();
  2265. }
  2266. return status;
  2267. }
  2268. /**************************************************************************\
  2269. *
  2270. * Function Description:
  2271. *
  2272. * convert a 2 segment closed subpath emitted by the region conversion
  2273. * to a correct winding path.
  2274. *
  2275. * Arguments:
  2276. *
  2277. * [IN] p - the path.
  2278. *
  2279. * Created:
  2280. *
  2281. * 09/21/2000 asecchia
  2282. * Created it.
  2283. *
  2284. \**************************************************************************/
  2285. struct PathBound
  2286. {
  2287. REAL xmin;
  2288. REAL ymin;
  2289. REAL xmax;
  2290. REAL ymax;
  2291. INT count;
  2292. GpPointF *points;
  2293. BYTE *types;
  2294. bool reverse;
  2295. void Init(INT c, GpPointF *p, BYTE *t)
  2296. {
  2297. reverse = false;
  2298. points = p;
  2299. types = t;
  2300. count = c;
  2301. }
  2302. };
  2303. void ComputeBoundingBox(
  2304. GpPathPointIterator &i,
  2305. PathBound *p
  2306. )
  2307. {
  2308. GpPointF *point = i.CurrentItem();
  2309. p->xmax = p->xmin = point->X;
  2310. p->ymax = p->ymin = point->Y;
  2311. while(!i.IsDone())
  2312. {
  2313. point = i.CurrentItem();
  2314. if(point->X < p->xmin) { p->xmin = point->X; }
  2315. if(point->X > p->xmax) { p->xmax = point->X; }
  2316. if(point->Y < p->ymin) { p->ymin = point->Y; }
  2317. if(point->Y > p->ymax) { p->ymax = point->Y; }
  2318. i.Next();
  2319. }
  2320. }
  2321. bool Contains(PathBound &pb1, PathBound &pb2)
  2322. {
  2323. return (
  2324. (pb1.xmin <= pb2.xmin) &&
  2325. (pb1.ymin <= pb2.ymin) &&
  2326. (pb1.xmax >= pb2.xmax) &&
  2327. (pb1.ymax >= pb2.ymax)
  2328. );
  2329. }
  2330. void ConvertRegionOutputToWinding(GpPath **p)
  2331. {
  2332. ASSERT(*p);
  2333. GpPathPointIterator iPoints(
  2334. (GpPointF*)(*p)->GetPathPoints(),
  2335. (BYTE*)(*p)->GetPathTypes(),
  2336. (*p)->GetPointCount()
  2337. );
  2338. GpSubpathIterator iSubpath(&iPoints);
  2339. GpPointF *points;
  2340. BYTE *types;
  2341. INT count;
  2342. GpPath *ret = new GpPath(FillModeWinding);
  2343. // if we're out of memory, simply give them back their path.
  2344. if(!ret) { return; }
  2345. GpPath *sub;
  2346. DynArray<PathBound> bounds;
  2347. PathBound pb;
  2348. // Iterate through all the subpaths culling information for the following
  2349. // algorithm. This is O(n) in the number of points.
  2350. // The information we need is the starting point for each subpath and
  2351. // the bounding box.
  2352. while(!iSubpath.IsDone())
  2353. {
  2354. count = -iSubpath.CurrentIndex();
  2355. points = iSubpath.CurrentItem();
  2356. types = iSubpath.CurrentType();
  2357. iSubpath.Next();
  2358. count += iSubpath.CurrentIndex();
  2359. GpPathPointIterator iSubpathPoint( points, types, count );
  2360. pb.Init(count, points, types);
  2361. ComputeBoundingBox( iSubpathPoint, &pb );
  2362. bounds.Add(pb);
  2363. }
  2364. // Double loop through all the subpaths figuring out the containment
  2365. // relationships.
  2366. // For every level of containment, flip the reverse bit.
  2367. // E.g. for a subpath that's contained by 5 other rectangles, start at
  2368. // false and apply 5x(!) !!!!!false == true which means flip this path.
  2369. // this is O(n^2) in the number of subpaths.
  2370. count = bounds.GetCount();
  2371. int i, j;
  2372. for(i=1; i<count; i++)
  2373. {
  2374. for(j=i-1; j>=0; j--)
  2375. {
  2376. if(Contains(bounds[i], bounds[j]))
  2377. {
  2378. bounds[j].reverse = !bounds[j].reverse;
  2379. continue;
  2380. }
  2381. if(Contains(bounds[j], bounds[i]))
  2382. {
  2383. bounds[i].reverse = !bounds[i].reverse;
  2384. }
  2385. }
  2386. }
  2387. // Now reverse all the subpaths that need to be reversed.
  2388. // Accumulate the results into the array.
  2389. for(i=0; i<count; i++)
  2390. {
  2391. sub = new GpPath(
  2392. bounds[i].points,
  2393. bounds[i].types,
  2394. bounds[i].count
  2395. );
  2396. if(!sub)
  2397. {
  2398. // if we failed to make our temporary path, stop and return
  2399. // what we've accumulated so far.
  2400. WARNING(("ran out of memory accumulating the path"));
  2401. break;
  2402. }
  2403. if(bounds[i].reverse)
  2404. {
  2405. sub->Reverse();
  2406. }
  2407. ret->AddPath(sub, FALSE);
  2408. delete sub;
  2409. }
  2410. delete *p;
  2411. *p = ret;
  2412. }
  2413. /**************************************************************************\
  2414. *
  2415. * Function Description:
  2416. *
  2417. * GetWidenedPath. Returns a widened version of the path. The path is widened
  2418. * according to the pen and the result is transformed according to the input
  2419. * matrix. When flattening the path the matrix is used as the world to device
  2420. * transform and the flatness tolerance is applied.
  2421. *
  2422. * This function handles inset and outset pen alignments.
  2423. *
  2424. * Arguments:
  2425. *
  2426. * [IN] pen - pen - specifies width for widening
  2427. * [IN] matrix - world to device transform.
  2428. * [IN] flatness - number of device pixels of error allowed for flattening
  2429. *
  2430. * Return:
  2431. *
  2432. * GpPath * - widened path. NULL on failure.
  2433. * - !!! this should return the path in an OUT parameter and
  2434. * - propagate the GpStatus correctly.
  2435. *
  2436. * Created:
  2437. *
  2438. * 09/31/2000 asecchia
  2439. * Rewrote it.
  2440. *
  2441. \**************************************************************************/
  2442. GpPath*
  2443. GpPath::GetWidenedPath(
  2444. const GpPen *pen,
  2445. const GpMatrix *matrix,
  2446. REAL flatness
  2447. ) const
  2448. {
  2449. ASSERT(pen);
  2450. // Redefine a NULL input matrix to be Identity for the duration of this
  2451. // routine.
  2452. GpMatrix transform;
  2453. if(!matrix)
  2454. {
  2455. matrix = &transform;
  2456. }
  2457. DpPen *internalPen = NULL;
  2458. internalPen = const_cast<GpPen*>(pen)->GetDevicePen();
  2459. ASSERT(internalPen);
  2460. if (internalPen->PenAlignment != PenAlignmentInset)
  2461. {
  2462. // Use the standard widening code for non-inset or non-outset pen.
  2463. return GetWidenedPathInternal(
  2464. internalPen,
  2465. matrix,
  2466. flatness,
  2467. FALSE // standard pen
  2468. );
  2469. }
  2470. else
  2471. {
  2472. // Do the Inset Pen.
  2473. // Our technique is as follows. See the inset pen spec in the
  2474. // gdiplus\specs directory.
  2475. // First, inset pen is defined as widening to the inside of the path
  2476. // which only has meaning for closed segments. Behaviour for open
  2477. // segments is unchanged (center pen).
  2478. // We widen the path at 2x the stroke width using a center pen.
  2479. // For round dash caps, we use a double-round or 'B' cap. We also
  2480. // mirror the compound line pattern across the spine of the path.
  2481. // Then we import the widened path as a region and clip against the
  2482. // original path converted to a region. What's left is a region
  2483. // which contains the widened inset pen. This is converted to a path
  2484. // and we're done.
  2485. // Copy the pen. Note that this will copy the *pointer* to the Brush
  2486. // but this is ok because the DpPen (insetPen) doesn't have a
  2487. // destructor and so won't attempt to free any state.
  2488. // We will need an insetPen for the closed subpath segments and a
  2489. // centerPen for the open subpath segments.
  2490. DpPen insetPen = *internalPen;
  2491. DpPen centerPen = *internalPen;
  2492. // Use a double width center pen and then clip off the outside creating
  2493. // a single width insetPen.
  2494. insetPen.Width *= 2.0f;
  2495. insetPen.PenAlignment = PenAlignmentCenter;
  2496. centerPen.PenAlignment = PenAlignmentCenter;
  2497. // Copy the compound array duplicating the compound array in reverse
  2498. // and rescaling back to [0,1] interval (i.e. mirror along the spine).
  2499. if( internalPen->CompoundCount > 0)
  2500. {
  2501. insetPen.CompoundArray = (REAL*)GpMalloc(
  2502. sizeof(REAL)*insetPen.CompoundCount*2
  2503. );
  2504. // Check the GpMalloc for out of memory.
  2505. if(insetPen.CompoundArray == NULL)
  2506. {
  2507. return NULL;
  2508. }
  2509. // Copy the pen->CompoundArray and duplicate it in reverse (mirror).
  2510. // rescale to the interval [0, 1]
  2511. for(INT i=0; i<insetPen.CompoundCount; i++)
  2512. {
  2513. // copy and scale range [0, 1] to [0, 0.5]
  2514. insetPen.CompoundArray[i] = internalPen->CompoundArray[i]/2.0f;
  2515. // copy and scale range [0, 1] to [0.5, 1] reversed.
  2516. insetPen.CompoundArray[insetPen.CompoundCount*2-i-1] =
  2517. 1.0f - internalPen->CompoundArray[i]/2.0f;
  2518. }
  2519. // we have double the number of entries now.
  2520. insetPen.CompoundCount *= 2;
  2521. }
  2522. // Create an iterator to step through each subpath.
  2523. GpPathPointIterator pathIterator(
  2524. (GpPointF*)GetPathPoints(),
  2525. (BYTE*)GetPathTypes(),
  2526. GetPointCount()
  2527. );
  2528. GpSubpathIterator subPathIterator(
  2529. &pathIterator
  2530. );
  2531. // Some temporary variables.
  2532. GpPointF *points;
  2533. BYTE *types;
  2534. INT subPathCount;
  2535. GpPath *widenedPath = NULL;
  2536. GpPath *subPath = NULL;
  2537. // Accumulate the widened sub paths in this returnPath.
  2538. GpPath *returnPath = new GpPath(FillModeWinding);
  2539. // loop while there are more subpaths and the returnPath is not NULL
  2540. // This implicitly checks that returnPath was allocated correctly.
  2541. while(returnPath && !subPathIterator.IsDone())
  2542. {
  2543. // Get the data for the current subpath.
  2544. points = subPathIterator.CurrentItem();
  2545. types = subPathIterator.CurrentType();
  2546. subPathCount = -subPathIterator.CurrentIndex();
  2547. subPathIterator.Next();
  2548. subPathCount += subPathIterator.CurrentIndex();
  2549. // Create a path object representing the current sub path.
  2550. subPath = new GpPath(points, types, subPathCount);
  2551. if(!subPath)
  2552. {
  2553. // failed the allocation.
  2554. delete returnPath;
  2555. returnPath = NULL;
  2556. break;
  2557. }
  2558. // Is this subpath closed?
  2559. BOOL isClosed = IsClosedType(types[subPathCount-1]);
  2560. // Widen the subPath with the inset pen for closed and
  2561. // center pen for open.
  2562. widenedPath = subPath->GetWidenedPathInternal(
  2563. (isClosed) ? &insetPen : &centerPen,
  2564. matrix,
  2565. flatness,
  2566. isClosed // Inset/Outset pen?
  2567. );
  2568. // don't need the subPath anymore - we have the widened version.
  2569. delete subPath;
  2570. subPath = NULL;
  2571. // Check if the widener succeeded.
  2572. if(!widenedPath || !widenedPath->IsValid())
  2573. {
  2574. delete widenedPath;
  2575. widenedPath = NULL;
  2576. delete returnPath;
  2577. returnPath = NULL;
  2578. break;
  2579. }
  2580. if(isClosed)
  2581. {
  2582. // Region to path.
  2583. // The widenedPath has already been transformed by the widener
  2584. // according to the matrix. Use the identity to convert the
  2585. // widenedPath to a region, but use the matrix to transform the
  2586. // (still untransformed) original matrix to a region.
  2587. GpMatrix identityMatrix;
  2588. const GpMatrix *scaleMatrix = &identityMatrix;
  2589. if(matrix)
  2590. {
  2591. scaleMatrix = matrix;
  2592. }
  2593. DpRegion srcRgn(widenedPath, &identityMatrix);
  2594. DpRegion clipRgn((DpPath*)(this), scaleMatrix);// const and type cast.
  2595. // Clip the region
  2596. GpStatus clip = Ok;
  2597. if(internalPen->PenAlignment == PenAlignmentInset)
  2598. {
  2599. // Inset pen is an And operation.
  2600. clip = srcRgn.And(&clipRgn);
  2601. }
  2602. GpPath *clippedPath;
  2603. if(clip == Ok)
  2604. {
  2605. clippedPath = new GpPath(&srcRgn);
  2606. if(!clippedPath)
  2607. {
  2608. delete widenedPath;
  2609. widenedPath = NULL;
  2610. delete returnPath;
  2611. returnPath = NULL;
  2612. break;
  2613. }
  2614. ConvertRegionOutputToWinding(&clippedPath);
  2615. // Accumulate the current subpath that we've just clipped
  2616. // for inset/outset into the final result.
  2617. returnPath->AddPath(clippedPath, FALSE);
  2618. delete clippedPath;
  2619. clippedPath = NULL;
  2620. }
  2621. }
  2622. else
  2623. {
  2624. // Accumulate the center pen widened path for the open
  2625. // subpath segment.
  2626. returnPath->AddPath(widenedPath, FALSE);
  2627. }
  2628. delete widenedPath;
  2629. widenedPath = NULL;
  2630. }
  2631. // clean up.
  2632. if(internalPen->CompoundCount > 0)
  2633. {
  2634. // we allocated a new piece of memory, throw it away.
  2635. // Make sure we're not trying to throw away the original pen
  2636. // CompoundArray - only free the temporary one if we created it.
  2637. ASSERT(insetPen.CompoundArray != internalPen->CompoundArray);
  2638. GpFree(insetPen.CompoundArray);
  2639. insetPen.CompoundArray = NULL;
  2640. }
  2641. return returnPath;
  2642. }
  2643. }
  2644. /**************************************************************************\
  2645. *
  2646. * Function Description:
  2647. *
  2648. * The sweep phase of a mark-sweep path point deletion algorithm
  2649. * This will delete all points marked with PathPointTypeInternalUse.
  2650. *
  2651. * If it deletes a start marker, it'll make the next valid point a start
  2652. * point.
  2653. *
  2654. * NOTE:
  2655. * If the algorithm encounters a closed subpath marker it will simply
  2656. * delete it. Because this algorithm is used for trimming the ends of
  2657. * open subpath segments (during endcapping), this is the desired behaviour,
  2658. * but may not be strictly correct for other uses.
  2659. *
  2660. * The points to be deleted are marked by oring in the
  2661. * PathPointTypeInternalUse flag. This flag is used by the widener as an
  2662. * internal flag and as a deletion mask for this code. These two usages
  2663. * do not (and should not) overlap.
  2664. *
  2665. * Created:
  2666. *
  2667. * 10/07/2000 asecchia
  2668. * created it.
  2669. *
  2670. \**************************************************************************/
  2671. VOID GpPath::EraseMarkedSegments()
  2672. {
  2673. // Get pointers to the source buffers.
  2674. GpPointF *dstPoints = Points.GetDataBuffer();
  2675. BYTE *dstTypes = Types.GetDataBuffer();
  2676. INT count = Points.GetCount();
  2677. INT delete_count = 0;
  2678. INT i=0;
  2679. GpPointF *srcPoints = dstPoints;
  2680. BYTE *srcTypes = dstTypes;
  2681. bool deleted_start_marker = false;
  2682. while(i<count)
  2683. {
  2684. // Skip all the points marked for deletion.
  2685. if((*srcTypes) & PathPointTypeInternalUse)
  2686. {
  2687. delete_count++;
  2688. // if we ever encounter a start marker, keep track of that fact.
  2689. deleted_start_marker |=
  2690. (((*srcTypes) & PathPointTypePathTypeMask) == PathPointTypeStart);
  2691. }
  2692. else
  2693. {
  2694. // If we have deleted some stuff, move the data up.
  2695. if(srcTypes!=dstTypes)
  2696. {
  2697. *dstPoints = *srcPoints;
  2698. *dstTypes = *srcTypes;
  2699. // if we deleted a start marker in the last deletion run,
  2700. // make the next non-deleted point a start marker.
  2701. // Note: if the whole subpath is marked for deletion and
  2702. // it's the last subpath, then we won't do this code because
  2703. // we'll terminate the while loop first. This protects against
  2704. // overwriting our buffer.
  2705. if(deleted_start_marker)
  2706. {
  2707. *dstTypes &= ~PathPointTypePathTypeMask;
  2708. *dstTypes |= PathPointTypeStart;
  2709. }
  2710. }
  2711. deleted_start_marker = false;
  2712. // increment to the next element.
  2713. dstPoints++;
  2714. dstTypes++;
  2715. }
  2716. // increment these every iteration through the loop.
  2717. srcTypes++;
  2718. srcPoints++;
  2719. i++;
  2720. }
  2721. // update the DynArrays so that they reflect the new (deleted) count.
  2722. Points.AdjustCount(-delete_count);
  2723. Types.AdjustCount(-delete_count);
  2724. InvalidateCache();
  2725. UpdateUid();
  2726. }
  2727. /**************************************************************************\
  2728. *
  2729. * Function Description:
  2730. *
  2731. * Returns a widened version of the path. This routine does not handle
  2732. * inset or outset pen alignments. The input insetPen parameter specifies
  2733. * if we should double widen the path in preparation for inset or outset
  2734. * pen modes. This has impact on dash caps and dash length.
  2735. *
  2736. * Return
  2737. *
  2738. * GpPath - the widened path. NULL if this routine fails.
  2739. *
  2740. * Arguments:
  2741. *
  2742. * [IN] pen - pen - specifies width for widening
  2743. * [IN] matrix - world to device transform.
  2744. * [IN] flatness - number of device pixels of error allowed for flattening
  2745. * [IN] insetPen - flag specifying if inset pen is being used.
  2746. *
  2747. * Return:
  2748. *
  2749. * GpPath * - widened path. NULL on failure.
  2750. * - !!! this should return the path in an OUT parameter and
  2751. * - propagate the GpStatus correctly.
  2752. *
  2753. * Created:
  2754. *
  2755. * 10/05/2000 asecchia
  2756. * rewrote it.
  2757. *
  2758. \**************************************************************************/
  2759. GpPath*
  2760. GpPath::GetWidenedPathInternal(
  2761. const DpPen *pen,
  2762. const GpMatrix *matrix,
  2763. REAL flatness,
  2764. BOOL insetPen
  2765. ) const
  2766. {
  2767. ASSERT(pen);
  2768. ASSERT(matrix);
  2769. ASSERT(pen->PenAlignment!=PenAlignmentInset);
  2770. GpStatus status = Ok;
  2771. GpMatrix invMatrix(*matrix);
  2772. if(Ok != invMatrix.Invert())
  2773. {
  2774. ONCE(WARNING(("GetWidenedPath: failed to invert the matrix")));
  2775. return NULL;
  2776. }
  2777. // This is a const function. We cannot modify 'this' so we clone
  2778. // the path in order to flatten it.
  2779. GpPath* path = this->Clone();
  2780. if(path == NULL) { return NULL; }
  2781. if(Ok != (status = path->Flatten(matrix, flatness)))
  2782. {
  2783. ONCE(WARNING(("GetWidenedPath: failed to flatten the path (%x)", status)));
  2784. return NULL;
  2785. }
  2786. // Undo the Flatten matrix transform so that we can widen in world space.
  2787. // This is required so that we correctly compute the device resolution
  2788. // minimum pen width for nominal pens.
  2789. path->Transform(&invMatrix);
  2790. // Do all the path decorations before widening. This is to ensure that
  2791. // the decorations have all of the original path information to operate
  2792. // on --- the widening/decoration process is lossy so they have to be
  2793. // performed in the right order.
  2794. // First apply the end caps. This decoration must be applied before
  2795. // dashing the path.
  2796. // Need to loop through all the subpaths, apply the end caps and
  2797. // fix up the path segments so they don't exit the cap incorrectly.
  2798. // put all the caps in a path for later use. We will apply these caps
  2799. // when we're done widening.
  2800. GpPath *caps = NULL;
  2801. if(GpEndCapCreator::PenNeedsEndCapCreator(pen))
  2802. {
  2803. // Create an instance of the GpEndCapCreator which will create
  2804. // our endcap aggregate path.
  2805. GpEndCapCreator ecc(
  2806. path,
  2807. const_cast<DpPen*>(pen),
  2808. matrix,
  2809. 0.0f, 0.0f,
  2810. TRUE
  2811. );
  2812. // CreateCapPath will mark the points in the path for deletion if
  2813. // it's necessary to trim the path to fit the cap.
  2814. status = ecc.CreateCapPath(&caps);
  2815. if(status != Ok)
  2816. {
  2817. return NULL;
  2818. }
  2819. // Remove the points marked for deletion in the cap trimming step.
  2820. path->EraseMarkedSegments();
  2821. }
  2822. // Apply the dash decorations. Note that this will bounce on an empty path.
  2823. GpPath* dashPath = NULL;
  2824. if( (pen) &&
  2825. (pen->DashStyle != DashStyleSolid) &&
  2826. (path->GetPointCount() > 0)
  2827. )
  2828. {
  2829. // the width is artificially expanded by 2 if the pen is inset.
  2830. // we need to factor this into the dash length and scale by 0.5.
  2831. dashPath = path->CreateDashedPath(
  2832. pen,
  2833. matrix,
  2834. 0.0f, // not used
  2835. 0.0f, // not used
  2836. (insetPen) ? 0.5f : 1.0f,
  2837. TRUE
  2838. );
  2839. // If we successfully got a dashed version of *path, delete
  2840. // the old one and return the new one.
  2841. if(dashPath)
  2842. {
  2843. delete path;
  2844. path = dashPath;
  2845. }
  2846. }
  2847. // Only do the widening if we have some points left in our
  2848. // path after trimming
  2849. if(path->GetPointCount() > 0)
  2850. {
  2851. // Create a widener object. Note that if path has no points left, this
  2852. // will bounce immediately with an invalid widener.
  2853. GpPathWidener widener(
  2854. path,
  2855. pen,
  2856. matrix,
  2857. 0.0f, // not used
  2858. 0.0f, // not used
  2859. TRUE, // not used
  2860. insetPen
  2861. );
  2862. // We're done with this now.
  2863. delete path;
  2864. path = NULL;
  2865. // Check if we have a valid Widener object.
  2866. if(!widener.IsValid())
  2867. {
  2868. status = OutOfMemory;
  2869. }
  2870. // Get the widened path.
  2871. if(status == Ok)
  2872. {
  2873. status = widener.Widen(&path);
  2874. }
  2875. }
  2876. else
  2877. {
  2878. delete path;
  2879. path = caps;
  2880. caps = NULL;
  2881. }
  2882. // paranoid checking the return from the widener.
  2883. if((status == Ok) && (path != NULL))
  2884. {
  2885. // Add the endcaps to the widened path. AddPath will bounce a NULL
  2886. // caps pointer with InvalidParameter. For our purposes that is
  2887. // considered correctly handled and we continue.
  2888. path->AddPath(caps, FALSE);
  2889. if(path->IsValid())
  2890. {
  2891. // Transform into the requested destination device space.
  2892. path->Transform(matrix);
  2893. }
  2894. }
  2895. // Delete the caps before returning. If we had caps, we've copied them
  2896. // into path, otherwise caps is NULL. Or we failed to widen. Either way
  2897. // we must not leak memory.
  2898. delete caps;
  2899. caps = NULL;
  2900. return path;
  2901. }
  2902. /**************************************************************************\
  2903. *
  2904. * Function Description:
  2905. *
  2906. * This widenes itself.
  2907. *
  2908. * Arguments:
  2909. *
  2910. * [IN] pen - the pen.
  2911. * [IN] matrix - Specifies the transform
  2912. * [IN] dpiX - the X-resolution.
  2913. * [IN] dpiY - the Y-resolution.
  2914. *
  2915. * Return Value:
  2916. *
  2917. * Ok if successfull.
  2918. *
  2919. * Created:
  2920. *
  2921. * 09/27/1999 ikkof
  2922. * Created it.
  2923. *
  2924. \**************************************************************************/
  2925. GpStatus
  2926. GpPath::Widen(
  2927. GpPen *pen,
  2928. GpMatrix *matrix,
  2929. REAL flatness
  2930. )
  2931. {
  2932. if(pen==NULL)
  2933. {
  2934. return InvalidParameter;
  2935. }
  2936. GpMatrix transform; // Identity matrix
  2937. if(matrix)
  2938. {
  2939. transform = *matrix;
  2940. }
  2941. GpPath* widenedPath = GetWidenedPath(
  2942. pen,
  2943. &transform,
  2944. flatness
  2945. );
  2946. if(widenedPath)
  2947. {
  2948. Points.Reset(FALSE);
  2949. Types.Reset(FALSE);
  2950. INT count = widenedPath->GetPointCount();
  2951. Points.AddMultiple(widenedPath->Points.GetDataBuffer(), count);
  2952. Types.AddMultiple(widenedPath->Types.GetDataBuffer(), count);
  2953. SubpathCount = widenedPath->SubpathCount;
  2954. HasBezier = widenedPath->HasBezier;
  2955. Flags = widenedPath->Flags;
  2956. FillMode = FillModeWinding;
  2957. delete widenedPath;
  2958. GpStatus status = Ok;
  2959. InvalidateCache();
  2960. UpdateUid();
  2961. return status;
  2962. }
  2963. else
  2964. {
  2965. return OutOfMemory;
  2966. }
  2967. }
  2968. // Get the flattened path.
  2969. const DpPath *
  2970. GpPath::GetFlattenedPath(
  2971. const GpMatrix* matrix,
  2972. DpEnumerationType type,
  2973. const DpPen* pen
  2974. ) const
  2975. {
  2976. GpPath* flattenedPath = NULL;
  2977. if(type == Flattened)
  2978. {
  2979. flattenedPath = Clone();
  2980. if(flattenedPath)
  2981. {
  2982. GpStatus status = flattenedPath->Flatten(matrix);
  2983. if(Ok != status)
  2984. {
  2985. // Out of memory or flatten returned some other error,
  2986. // however we can't return a status code from this routine.
  2987. delete flattenedPath;
  2988. flattenedPath = NULL;
  2989. }
  2990. }
  2991. }
  2992. else if(type == Widened)
  2993. {
  2994. flattenedPath = GetWidenedPath(
  2995. GpPen::GetPen(pen),
  2996. matrix,
  2997. FlatnessDefault
  2998. );
  2999. }
  3000. return flattenedPath;
  3001. }
  3002. /**************************************************************************\
  3003. *
  3004. * Function Description:
  3005. *
  3006. * Checks if the given point in World coordinate is inside of
  3007. * the path. The matrix is used to render path in specific resolution.
  3008. * Usually, Graphics's World to Device matrix is used. If matrix is NULL,
  3009. * the identity matrix is used.
  3010. *
  3011. * Arguments:
  3012. *
  3013. * [IN] point - A test point in World coordinate
  3014. * [OUT] isVisible - TRUE is the test point is inside of the path.
  3015. * [IN] matrix - A matrix to render path. Identity is used if NULL.
  3016. *
  3017. * Return Value:
  3018. *
  3019. * Ok if successfull.
  3020. *
  3021. * Created:
  3022. *
  3023. * 10/05/1999 ikkof
  3024. * Created it.
  3025. *
  3026. \**************************************************************************/
  3027. GpStatus
  3028. GpPath::IsVisible(
  3029. GpPointF* point,
  3030. BOOL* isVisible,
  3031. GpMatrix* matrix)
  3032. {
  3033. GpMatrix m;
  3034. if(matrix)
  3035. m = *matrix;
  3036. GpRegion rgn(this);
  3037. if(rgn.IsValid())
  3038. return rgn.IsVisible(point, &m, isVisible);
  3039. *isVisible = FALSE;
  3040. return GenericError;
  3041. }
  3042. /**************************************************************************\
  3043. *
  3044. * Function Description:
  3045. *
  3046. * Checks if the given point in World coordinate is inside of
  3047. * the path outline. The matrix is used to render path in specific resolution.
  3048. * Usually, Graphics's World to Device matrix is used. If matrix is NULL,
  3049. * the identity matrix is used.
  3050. *
  3051. * Arguments:
  3052. *
  3053. * [IN] point - A test point in World coordinate
  3054. * [OUT] isVisible - TRUE is the test point is inside of the path.
  3055. * [IN] pen - A pen to draw the outline.
  3056. * [IN] matrix - A matrix to render path. Identity is used if NULL.
  3057. * [IN] dpiX - x-resolution of the device.
  3058. * [IN] dpiY - y-resolution of the device.
  3059. *
  3060. * Return Value:
  3061. *
  3062. * Ok if successfull.
  3063. *
  3064. * Created:
  3065. *
  3066. * 10/05/1999 ikkof
  3067. * Created it.
  3068. *
  3069. \**************************************************************************/
  3070. GpStatus
  3071. GpPath::IsOutlineVisible(
  3072. GpPointF* point,
  3073. BOOL* isVisible,
  3074. GpPen* pen,
  3075. GpMatrix* matrix,
  3076. REAL dpiX,
  3077. REAL dpiY
  3078. )
  3079. {
  3080. if ((dpiX <= 0) || (dpiY <= 0))
  3081. {
  3082. dpiX = Globals::DesktopDpiX;
  3083. dpiY = Globals::DesktopDpiY;
  3084. }
  3085. // If the given pen is not a solid line,
  3086. // clone the pen and set its dash type to Solid.
  3087. // We do line hit testing in solid lines.
  3088. GpPen* pen1 = NULL;
  3089. if(pen && pen->GetDashStyle() != DashStyleSolid)
  3090. {
  3091. pen1 = pen->Clone();
  3092. if(pen1)
  3093. pen1->SetDashStyle(DashStyleSolid);
  3094. }
  3095. else
  3096. pen1 = pen;
  3097. if(pen1 == NULL)
  3098. {
  3099. *isVisible = FALSE;
  3100. return Ok;
  3101. }
  3102. // Create a widened path in the transformed coordinates.
  3103. GpPath* widenedPath = GetWidenedPath(
  3104. pen1,
  3105. matrix,
  3106. FlatnessDefault
  3107. );
  3108. if(pen1 != pen)
  3109. delete pen1;
  3110. GpStatus status = Ok;
  3111. if(widenedPath)
  3112. {
  3113. // Since the widened path is already transformed, we have to
  3114. // transform the given point.
  3115. GpPointF transformedPoint = *point;
  3116. if(matrix)
  3117. matrix->Transform(&transformedPoint);
  3118. status = widenedPath->IsVisible(&transformedPoint, isVisible, NULL);
  3119. delete widenedPath;
  3120. }
  3121. else
  3122. {
  3123. *isVisible = FALSE;
  3124. }
  3125. return status;
  3126. }
  3127. // Is the current dash segment a line segment?
  3128. // If false it's a space segment.
  3129. inline bool IsLineSegment(GpIterator<REAL> &dashIt)
  3130. {
  3131. // line segment starts on even indices.
  3132. return bool( !(dashIt.CurrentIndex() & 0x1) );
  3133. }
  3134. // Emit a line segment if it is not degenerate.
  3135. // Return true if emitted, false if degenerate
  3136. bool EmitLineSegment(
  3137. GpPathPointIterator &dstPath,
  3138. GpPointF p0,
  3139. GpPointF p1,
  3140. bool isLineStart
  3141. )
  3142. {
  3143. GpPointF *currentPoint;
  3144. BYTE *currentType;
  3145. if( (REALABS(p0.X-p1.X) < REAL_EPSILON) &&
  3146. (REALABS(p0.Y-p1.Y) < REAL_EPSILON) )
  3147. {
  3148. // Don't emit a line segment if it has zero length.
  3149. return false;
  3150. }
  3151. // If the last emitted line ends at the same point that this next
  3152. // one starts, we don't need a new start record.
  3153. if(isLineStart)
  3154. {
  3155. // start point.
  3156. currentPoint = dstPath.CurrentItem();
  3157. *currentPoint = p0;
  3158. currentType = dstPath.CurrentType();
  3159. *currentType = PathPointTypeStart | PathPointTypeDashMode;
  3160. dstPath.Next();
  3161. }
  3162. // end point.
  3163. currentPoint = dstPath.CurrentItem();
  3164. *currentPoint = p1;
  3165. currentType = dstPath.CurrentType();
  3166. *currentType = PathPointTypeLine | PathPointTypeDashMode;
  3167. dstPath.Next();
  3168. return true;
  3169. }
  3170. INT
  3171. getDashData(
  3172. BYTE* newTypes,
  3173. GpPointF* newPts,
  3174. INT estimateCount,
  3175. REAL penWidth,
  3176. REAL dashOffset,
  3177. const REAL* dashArray,
  3178. INT dashCount,
  3179. const BYTE* types,
  3180. const GpPointF* points,
  3181. INT numOfPoints,
  3182. BOOL isClosed,
  3183. const REAL* distances
  3184. )
  3185. {
  3186. ASSERT(estimateCount >= numOfPoints);
  3187. ASSERT(types && points);
  3188. // Code assumes first point != last point for closed paths. If first
  3189. // point == last point, decrease point count
  3190. if (isClosed && numOfPoints &&
  3191. points[0].X == points[numOfPoints-1].X &&
  3192. points[0].Y == points[numOfPoints-1].Y)
  3193. {
  3194. numOfPoints--;
  3195. }
  3196. if(!newTypes || !newPts)
  3197. {
  3198. return 0;
  3199. }
  3200. // Make the iterators.
  3201. GpArrayIterator<GpPointF> pathIterator(
  3202. const_cast<GpPointF*>(points),
  3203. numOfPoints
  3204. );
  3205. GpArrayIterator<REAL> pathBaseDistance(
  3206. const_cast<REAL*>(distances),
  3207. numOfPoints
  3208. );
  3209. GpPathPointIterator dstPath(newPts, newTypes, estimateCount);
  3210. GpArrayIterator<REAL> dashBaseIterator(
  3211. const_cast<REAL*>(dashArray),
  3212. dashCount
  3213. );
  3214. // Compute the length of the dash
  3215. REAL dashLength = 0.0f;
  3216. while(!dashBaseIterator.IsDone())
  3217. {
  3218. dashLength += *(dashBaseIterator.CurrentItem());
  3219. dashBaseIterator.Next();
  3220. }
  3221. ASSERT(dashLength > -REAL_EPSILON);
  3222. // Do the offset initialization.
  3223. dashBaseIterator.SeekFirst();
  3224. REAL distance = GpModF(dashOffset, dashLength);
  3225. REAL delta;
  3226. // Compute the position in the dash array corresponding to the
  3227. // specified offset.
  3228. while(!dashBaseIterator.IsDone())
  3229. {
  3230. delta = *(dashBaseIterator.CurrentItem());
  3231. if(distance < delta)
  3232. {
  3233. // set to the remaining piece of the dash.
  3234. distance = delta-distance;
  3235. break;
  3236. }
  3237. distance -= delta;
  3238. dashBaseIterator.Next();
  3239. }
  3240. // The dashIterator is now set to point to the correct
  3241. // dash for the first segment.
  3242. // These are circular arrays to repeat the dash pattern.
  3243. GpCircularIterator<REAL> dashIterator(&dashBaseIterator);
  3244. // This is the distance into the current dash segment that we're going
  3245. // to start at.
  3246. REAL currentDashLength = distance;
  3247. REAL currentSegmentLength;
  3248. GpPointF p0, p1;
  3249. GpVector2D sD; // segment direction.
  3250. // Used to track if we need to emit a segment start record.
  3251. bool emittedPathSegment = false;
  3252. if(isClosed)
  3253. {
  3254. // set up everything off the last item and then point to
  3255. // the first item to start the process.
  3256. pathBaseDistance.SeekFirst();
  3257. pathIterator.SeekLast();
  3258. p0 = *(pathIterator.CurrentItem());
  3259. pathIterator.SeekFirst();
  3260. p1 = *(pathIterator.CurrentItem());
  3261. // get the distance between the first and last points.
  3262. GpVector2D seg = p1-p0;
  3263. currentSegmentLength = seg.Norm();
  3264. }
  3265. else
  3266. {
  3267. // Get the first point in the array.
  3268. p0 = *(pathIterator.CurrentItem());
  3269. // already initialized to the first point, start on the next one.
  3270. pathIterator.Next();
  3271. pathBaseDistance.Next();
  3272. // distance between point n and point n+1 is stored in
  3273. // distance[n+1]. distance[0] is the distance between the first
  3274. // and last points.
  3275. currentSegmentLength = *(pathBaseDistance.CurrentItem());
  3276. }
  3277. // reference the distances as circular so that we can simplify the
  3278. // internal algorithm by not having to check when we query for the
  3279. // next segment in the last iteration of the loop.
  3280. GpCircularIterator<REAL> pathDistance(&pathBaseDistance);
  3281. while( !pathIterator.IsDone() )
  3282. {
  3283. if(currentDashLength > currentSegmentLength)
  3284. {
  3285. // The remaining dash segment length is longer than the remaining
  3286. // path segment length.
  3287. // Finish the path segment.
  3288. // Note that we've moved along the dash segment.
  3289. currentDashLength -= currentSegmentLength;
  3290. p1 = *(pathIterator.CurrentItem());
  3291. if(IsLineSegment(dashIterator))
  3292. {
  3293. // emit a line. Add the start record only if we didn't just
  3294. // emit a path segment. If we're emitting a series of path
  3295. // segments to complete one dash, we can't have any start
  3296. // records inbetween the segments otherwise we'll end up with
  3297. // spurious endcaps in the middle of the lines.
  3298. emittedPathSegment = EmitLineSegment(
  3299. dstPath,
  3300. p0, p1,
  3301. !emittedPathSegment
  3302. );
  3303. }
  3304. else
  3305. {
  3306. emittedPathSegment = false;
  3307. }
  3308. p0 = p1;
  3309. // Keep these two in sync.
  3310. pathDistance.Next();
  3311. pathIterator.Next();
  3312. currentSegmentLength = *(pathDistance.CurrentItem());
  3313. }
  3314. else
  3315. {
  3316. // The remaining path segment length is longer than the remaining
  3317. // dash segment length.
  3318. // Finish the dash segment.
  3319. // Compute position between start and end point of the current
  3320. // path segment where we finish with this dash segment.
  3321. ASSERT(REALABS(currentSegmentLength)>REAL_EPSILON);
  3322. sD = *(pathIterator.CurrentItem());
  3323. sD -= p0;
  3324. sD *= currentDashLength/currentSegmentLength;
  3325. // Move along the path segment by the amount left in the
  3326. // dash segment.
  3327. currentSegmentLength -= currentDashLength;
  3328. p1 = p0 + sD;
  3329. if(IsLineSegment(dashIterator))
  3330. {
  3331. // emit a line. Add the start record only if we didn't just
  3332. // emit a path segment.
  3333. EmitLineSegment(
  3334. dstPath,
  3335. p0, p1,
  3336. !emittedPathSegment
  3337. );
  3338. }
  3339. p0 = p1;
  3340. // dashIterator is circular, so this should keep wrapping through
  3341. // the dash array.
  3342. dashIterator.Next();
  3343. // Get the new dash length.
  3344. currentDashLength = *(dashIterator.CurrentItem());
  3345. emittedPathSegment = false;
  3346. }
  3347. }
  3348. INT size = dstPath.CurrentIndex();
  3349. if(!isClosed && size != 0 && numOfPoints != 0)
  3350. {
  3351. BYTE *type;
  3352. REAL halfPenWidth2 = penWidth * penWidth / 4.0f;
  3353. // Turn off dash mode for the last segment if the last
  3354. // point is very close to the last dash segment.
  3355. if (distance_squared(points[numOfPoints-1], newPts[size-1]) < halfPenWidth2)
  3356. {
  3357. dstPath.Prev();
  3358. type = dstPath.CurrentType();
  3359. *type &= ~PathPointTypeDashMode;
  3360. }
  3361. // Turn off dash mode for the first segment if the first
  3362. // point is very close to the first dash segment.
  3363. if (distance_squared(points[0], newPts[0]) < halfPenWidth2)
  3364. {
  3365. dstPath.SeekFirst();
  3366. type = dstPath.CurrentType();
  3367. *type &= ~PathPointTypeDashMode;
  3368. }
  3369. }
  3370. // return the number of entries added to the dstPath array.
  3371. return (size);
  3372. }
  3373. /**************************************************************************\
  3374. *
  3375. * Function Description:
  3376. *
  3377. * Creates a dashed path.
  3378. *
  3379. * Arguments:
  3380. *
  3381. * [IN] pen - This pen contains the dash info.
  3382. * [IN] matrix - The transformation where the dash patterns are calculated.
  3383. * But the dashed path is transformed back to the World
  3384. * coordinates.
  3385. * [IN] dpiX - x-resolution.
  3386. * [IN] dpiY - y-resolution.
  3387. *
  3388. * Return Value:
  3389. *
  3390. * returns a dashed path.
  3391. *
  3392. * Created:
  3393. *
  3394. * 01/27/2000 ikkof
  3395. * Created it.
  3396. *
  3397. \**************************************************************************/
  3398. GpPath*
  3399. GpPath::CreateDashedPath(
  3400. const GpPen* pen,
  3401. const GpMatrix* matrix,
  3402. REAL dpiX,
  3403. REAL dpiY,
  3404. REAL dashScale,
  3405. BOOL needDashCaps
  3406. ) const
  3407. {
  3408. if(pen == NULL)
  3409. return NULL;
  3410. DpPen* dpPen = ((GpPen* ) pen)->GetDevicePen();
  3411. return CreateDashedPath(dpPen, matrix, dpiX, dpiY, dashScale, needDashCaps);
  3412. }
  3413. /**************************************************************************\
  3414. *
  3415. * Function Description:
  3416. *
  3417. * Returns TRUE if the given points have non-horizontal or non-vertical
  3418. * edges.
  3419. *
  3420. *
  3421. * Created:
  3422. *
  3423. * 04/07/2000 ikkof
  3424. * Created it.
  3425. *
  3426. \**************************************************************************/
  3427. inline
  3428. BOOL
  3429. hasDiagonalEdges(
  3430. GpPointF* points,
  3431. INT count
  3432. )
  3433. {
  3434. if(!points || count <= 1)
  3435. return FALSE;
  3436. GpPointF *curPt, *nextPt;
  3437. curPt = points;
  3438. nextPt = points + 1;
  3439. BOOL foundDiagonal = FALSE;
  3440. INT i = 1;
  3441. while(!foundDiagonal && i < count)
  3442. {
  3443. if((curPt->X == nextPt->X) || (curPt->Y == nextPt->Y))
  3444. {
  3445. // This is either horizontal or vertical edges.
  3446. // Go to the next edge.
  3447. curPt++;
  3448. nextPt++;
  3449. i++;
  3450. }
  3451. else
  3452. foundDiagonal = TRUE;
  3453. }
  3454. return foundDiagonal;
  3455. }
  3456. GpPath*
  3457. GpPath::CreateDashedPath(
  3458. const DpPen* dpPen,
  3459. const GpMatrix* matrix,
  3460. REAL dpiX,
  3461. REAL dpiY,
  3462. REAL dashScale,
  3463. BOOL needDashCaps
  3464. ) const
  3465. {
  3466. FPUStateSaver::AssertMode();
  3467. GpPointF* points = Points.GetDataBuffer();
  3468. INT numOfPoints = GetPointCount();
  3469. if(dpPen == NULL)
  3470. return NULL;
  3471. if(
  3472. dpPen->DashStyle == DashStyleSolid ||
  3473. dpPen->DashCount == 0 ||
  3474. dpPen->DashArray == NULL
  3475. )
  3476. return NULL;
  3477. REAL penWidth = dpPen->Width;
  3478. GpUnit unit = dpPen->Unit;
  3479. BOOL isWorldUnit = TRUE;
  3480. REAL dashUnit;
  3481. {
  3482. // The minimum pen width
  3483. REAL minimumPenWidth = 1.0f;
  3484. if(REALABS(dashScale-0.5f) < REAL_EPSILON)
  3485. {
  3486. minimumPenWidth = 4.0f;
  3487. }
  3488. if(unit != UnitWorld)
  3489. {
  3490. isWorldUnit = FALSE;
  3491. penWidth = ::GetDeviceWidth(penWidth, unit, dpiX);
  3492. // Prevent the extremely thin line and dashes.
  3493. dashUnit = max(penWidth, minimumPenWidth);
  3494. }
  3495. else
  3496. {
  3497. REAL majorR, minorR;
  3498. // Calculate the device width.
  3499. ::GetMajorAndMinorAxis(&majorR, &minorR, matrix);
  3500. REAL maxWidth = penWidth*majorR;
  3501. REAL minWidth = penWidth*minorR;
  3502. // If the device width becomes less than 1, strech the penWidth
  3503. // so that the device width becomes 1.
  3504. // If we're doing the inset pen, then the path is scaled up double
  3505. // in size and we need to scale by the inverse.
  3506. // Also, the minimum pen width needs to be 2 not 1 in this case
  3507. // because we will remove half the line width. dashScale is 1/2
  3508. // in this case so we divide by it.
  3509. dashUnit = penWidth;
  3510. if(maxWidth < minimumPenWidth)
  3511. {
  3512. dashUnit = minimumPenWidth/majorR;
  3513. }
  3514. }
  3515. }
  3516. dashUnit *= dashScale;
  3517. GpMatrix mat, invMat;
  3518. if(matrix)
  3519. {
  3520. mat = *matrix;
  3521. invMat = mat;
  3522. }
  3523. if(invMat.IsInvertible())
  3524. {
  3525. invMat.Invert();
  3526. }
  3527. else
  3528. {
  3529. WARNING(("Inverse matrix does not exist."));
  3530. return NULL;
  3531. }
  3532. INT dashCount = dpPen->DashCount;
  3533. REAL* dashArray = (REAL*) GpMalloc(dashCount*sizeof(REAL));
  3534. if(dashArray)
  3535. {
  3536. GpMemcpy(dashArray, dpPen->DashArray, dashCount*sizeof(REAL));
  3537. // Adjust the dash interval according the stroke width.
  3538. for(INT i = 0; i < dashCount; i++)
  3539. {
  3540. dashArray[i] *= dashUnit;
  3541. }
  3542. }
  3543. else
  3544. {
  3545. return NULL;
  3546. }
  3547. GpPath* newPath = Clone();
  3548. if(newPath && newPath->IsValid())
  3549. {
  3550. // Flatten in the resolution given by the matrix.
  3551. newPath->Flatten(&mat);
  3552. if(isWorldUnit)
  3553. {
  3554. // Transform back to the World Unit.
  3555. // When the pen is in WorldUnit, transform the path
  3556. // before detDashData() is called.
  3557. newPath->Transform(&invMat);
  3558. }
  3559. BYTE *types = newPath->Types.GetDataBuffer();
  3560. points = newPath->Points.GetDataBuffer();
  3561. numOfPoints = newPath->GetPointCount();
  3562. GpPointF* grad = (GpPointF*) GpMalloc((numOfPoints + 1)*sizeof(GpPointF));
  3563. REAL* distances = (REAL*) GpMalloc((numOfPoints + 1)*sizeof(REAL));
  3564. if(grad == NULL || distances == NULL)
  3565. {
  3566. GpFree(grad);
  3567. GpFree(dashArray);
  3568. delete newPath;
  3569. return NULL;
  3570. }
  3571. // Calculate the distance of each segment.
  3572. INT i;
  3573. REAL dashLength = 0;
  3574. for(i = 0; i < dashCount; i++)
  3575. dashLength += dashArray[i];
  3576. // Make sure count is an even number.
  3577. // !!! [asecchia] this looks completely bogus.
  3578. // surely we should have ASSERTed that this is true at this point
  3579. // and ensured that it was true by parameter validation at the API?
  3580. if(dashCount & 0x01)
  3581. dashCount ++;
  3582. // Compute the dash adjustment for the dash cap length here. This
  3583. // is outside of the subpath loop so it only gets computed once
  3584. // and therefore doesn't get applied to further subpath segments
  3585. // multiple times.
  3586. if (needDashCaps)
  3587. {
  3588. GpPen *gppen = GpPen::GetPen(dpPen);
  3589. if (gppen != NULL)
  3590. {
  3591. gppen->AdjustDashArrayForCaps(
  3592. dashUnit,
  3593. dashArray,
  3594. dashCount
  3595. );
  3596. }
  3597. }
  3598. DynByteArray dashTypes;
  3599. DynPointFArray dashPoints;
  3600. BYTE* newTypes = NULL;
  3601. GpPointF* newPts = NULL;
  3602. DpPathIterator iter(points, types, numOfPoints);
  3603. INT startIndex, endIndex;
  3604. BOOL isClosed;
  3605. REALD totalLength = 0;
  3606. INT totalCount = 0;
  3607. BOOL isSingleSubpath = iter.GetSubpathCount() == 1;
  3608. while(iter.NextSubpath(&startIndex, &endIndex, &isClosed))
  3609. {
  3610. GpPointF startPt, lastPt, nextPt;
  3611. REAL dx, dy;
  3612. REALD length;
  3613. startPt = points[startIndex];
  3614. lastPt = startPt;
  3615. totalLength = 0;
  3616. INT k = 0;
  3617. INT segmentCount = endIndex - startIndex + 1;
  3618. CalculateGradientArray(grad, distances,
  3619. points + startIndex, segmentCount);
  3620. for(i = 1; i < segmentCount; i++)
  3621. totalLength += distances[i];
  3622. if(isClosed)
  3623. totalLength += distances[0];
  3624. // Estimate the required points.
  3625. INT estimateCount
  3626. = GpCeiling(TOREAL(totalLength*dashCount/dashLength))
  3627. + numOfPoints;
  3628. // For extra caution, multiply by 2.
  3629. estimateCount <<= 1;
  3630. // Allocate new types and buffers
  3631. if(newTypes)
  3632. {
  3633. BYTE* newTypes1 = (BYTE*) GpRealloc(
  3634. newTypes,
  3635. estimateCount*sizeof(BYTE));
  3636. if(newTypes1)
  3637. newTypes = newTypes1;
  3638. else
  3639. goto cleanUp;
  3640. }
  3641. else
  3642. {
  3643. newTypes = (BYTE*) GpMalloc(estimateCount*sizeof(BYTE));
  3644. if(!newTypes)
  3645. goto cleanUp;
  3646. }
  3647. if(newPts)
  3648. {
  3649. GpPointF* newPts1 = (GpPointF*) GpRealloc(
  3650. newPts,
  3651. estimateCount*sizeof(GpPointF));
  3652. if(newPts1)
  3653. newPts = newPts1;
  3654. else
  3655. goto cleanUp;
  3656. }
  3657. else
  3658. {
  3659. newPts = (GpPointF*) GpMalloc(estimateCount*sizeof(GpPointF));
  3660. if(!newPts)
  3661. goto cleanUp;
  3662. }
  3663. // Adjust the dash offset if necessary.
  3664. REAL dashCapOffsetAdjustment = 0.0f;
  3665. if (needDashCaps)
  3666. {
  3667. GpPen *gppen = GpPen::GetPen(dpPen);
  3668. if ((gppen != NULL) && isClosed)
  3669. {
  3670. // Fix for Whistler Bug 178774
  3671. // Since dash caps are no longer 'inset' when they are
  3672. // rendered, it is possible that on closed paths, the dash caps
  3673. // on the start and end of a closed path will overlap. This
  3674. // offset will leave sufficient space for the two caps. However,
  3675. // this fix is not bullet-proof. It will *always* work if the
  3676. // Dash Offset is 0. However, if it is non-zero, it is possible
  3677. // that the offset will counter-act the adjustment and there
  3678. // will be some dash overlap at the start/end of closed paths.
  3679. // I believe this is acceptable since VISIO 2000, Office9 and
  3680. // PhotoDraw 2000 v2 also have the collision problem.
  3681. // The real solution is to enforce a minimum spacing between the
  3682. // start and end or merge the start/end segments if they collide.
  3683. dashCapOffsetAdjustment =
  3684. 2.0f * gppen->GetDashCapInsetLength(dashUnit);
  3685. }
  3686. }
  3687. INT newCount = getDashData(
  3688. newTypes,
  3689. newPts,
  3690. estimateCount,
  3691. penWidth,
  3692. // Shouldn't the offset be scaled dashUnit instead of penWidth?
  3693. dpPen->DashOffset * penWidth - dashCapOffsetAdjustment,
  3694. dashArray,
  3695. dashCount,
  3696. types + startIndex,
  3697. points + startIndex,
  3698. endIndex - startIndex + 1,
  3699. isClosed,
  3700. distances
  3701. );
  3702. if(newCount)
  3703. {
  3704. dashTypes.AddMultiple(newTypes, newCount);
  3705. dashPoints.AddMultiple(newPts, newCount);
  3706. }
  3707. }
  3708. totalCount = dashPoints.GetCount();
  3709. if(totalCount > 0)
  3710. {
  3711. GpPathData pathData;
  3712. pathData.Count = totalCount;
  3713. pathData.Types = dashTypes.GetDataBuffer();
  3714. pathData.Points = dashPoints.GetDataBuffer();
  3715. newPath->SetPathData(&pathData);
  3716. if(!isWorldUnit)
  3717. {
  3718. // Transform back to the World Unit.
  3719. // When the pen is in WorldUnit, it is already transformed
  3720. // before detDashData() is called.
  3721. newPath->Transform(&invMat);
  3722. }
  3723. }
  3724. else
  3725. {
  3726. delete newPath;
  3727. newPath = NULL;
  3728. }
  3729. GpFree(newTypes);
  3730. GpFree(newPts);
  3731. GpFree(distances);
  3732. GpFree(grad);
  3733. GpFree(dashArray);
  3734. return newPath;
  3735. cleanUp:
  3736. GpFree(newTypes);
  3737. GpFree(newPts);
  3738. GpFree(distances);
  3739. GpFree(grad);
  3740. GpFree(dashArray);
  3741. delete newPath;
  3742. return NULL;
  3743. }
  3744. else
  3745. {
  3746. GpFree(dashArray);
  3747. if(newPath)
  3748. delete newPath;
  3749. return NULL;
  3750. }
  3751. }
  3752. /**************************************************************************\
  3753. *
  3754. * Function Description
  3755. *
  3756. * ComputeWindingModeOutline
  3757. * (so called RemoveSelfIntersections)
  3758. *
  3759. * This computes the winding mode fill outline for the path. It's designed to
  3760. * produce a path that will look the same as a winding mode fill if it's
  3761. * filled with an alternate fill.
  3762. *
  3763. * Arguments:
  3764. *
  3765. * matrix - world to device matrix - used for flattening.
  3766. * flatness - number of device pixels of error in the flattening tolerance.
  3767. * - Pass FlatnessDefault for default behaviour.
  3768. *
  3769. * Return Value:
  3770. *
  3771. * GpStatus
  3772. *
  3773. * History:
  3774. *
  3775. * 06/16/1999 t-wehunt
  3776. * Created it.
  3777. * 10/31/2000 asecchia
  3778. * rewrote, rename.
  3779. *
  3780. \**************************************************************************/
  3781. GpStatus
  3782. GpPath::ComputeWindingModeOutline(
  3783. const GpMatrix *matrix,
  3784. REAL flatness,
  3785. BOOL *wereIntersectsRemoved
  3786. )
  3787. {
  3788. PathSelfIntersectRemover corrector;
  3789. DynPointFArray newPoints; // Array that will hold the new points.
  3790. DynIntArray polyCounts; // Array that will hold the numPoints for each
  3791. // new polygon.
  3792. INT numPolys; // count of new polygons created
  3793. INT numPoints; // count of new points created
  3794. GpStatus status = Ok; // holds return status of commmands
  3795. // Must clone the path because this could fail while we're accumulating
  3796. // the new path and we'd end up returning an invalid path.
  3797. // If we return InvalidParameter or some other failure code, we'll return
  3798. // with the original path intact.
  3799. GpPath *path = Clone();
  3800. if(path == NULL)
  3801. {
  3802. return OutOfMemory;
  3803. }
  3804. status = path->Flatten(matrix, flatness);
  3805. if(Ok != status)
  3806. {
  3807. goto Done;
  3808. }
  3809. INT pointCount = path->GetPointCount();
  3810. GpPointF *pathPts = const_cast<GpPointF*>(path->GetPathPoints());
  3811. BYTE *pathTypes = const_cast<BYTE*>(path->GetPathTypes());
  3812. if (pointCount == 0)
  3813. {
  3814. goto Done;
  3815. }
  3816. // Add the subpaths to the Path corrector
  3817. INT ptIndex=0; // ptIndex tracks the current index in the array of points.
  3818. INT count=0; // the size of the current subpath.
  3819. // Init the corrector with the number of points we will be adding.
  3820. if ((status = corrector.Init(pointCount)) != Ok)
  3821. {
  3822. goto Done;
  3823. }
  3824. while (ptIndex < pointCount)
  3825. {
  3826. if (pathTypes[ptIndex] == PathPointTypeStart && ptIndex != 0)
  3827. {
  3828. // Add the next subpath to the PathCorrector. the start index of the subpath is
  3829. // determined using the current index minus the current subPath size.
  3830. if ((status =
  3831. corrector.AddPolygon(pathPts + ptIndex-count, count)) != Ok)
  3832. {
  3833. goto Done;
  3834. }
  3835. // set count to 1 since this is the first point in the new subpath
  3836. count = 1;
  3837. } else
  3838. {
  3839. count++;
  3840. }
  3841. ptIndex++;
  3842. }
  3843. // Add the last subpath that is implicitly ended by the last point.
  3844. if (ptIndex != 0)
  3845. {
  3846. // Add the next subpath to the PathCorrector. the start index of the subpath is
  3847. // determined using the current index minus the current subPath size.
  3848. if ((status =
  3849. corrector.AddPolygon(pathPts + ptIndex-count, count)) != Ok)
  3850. {
  3851. goto Done;
  3852. }
  3853. }
  3854. if ((status = corrector.RemoveSelfIntersects()) != Ok)
  3855. {
  3856. goto Done;
  3857. }
  3858. if ((status = corrector.GetNewPoints(&newPoints, &polyCounts)) != Ok)
  3859. {
  3860. goto Done;
  3861. }
  3862. // clear out the old path data so we can replace with the newly corrected one.
  3863. path->Reset();
  3864. // Now that we have the corrected path, add it back.
  3865. GpPointF *curPoints = newPoints.GetDataBuffer();
  3866. for (INT i=0;i<polyCounts.GetCount();i++)
  3867. {
  3868. if(polyCounts[i] > 1)
  3869. {
  3870. if ((status = path->AddPolygon(curPoints,polyCounts[i])) != Ok)
  3871. {
  3872. goto Done;
  3873. }
  3874. }
  3875. else
  3876. {
  3877. WARNING(("degenerate polygon created by the SelfIntersectRemover"));
  3878. }
  3879. curPoints += polyCounts[i];
  3880. }
  3881. if (wereIntersectsRemoved != NULL)
  3882. {
  3883. *wereIntersectsRemoved = corrector.PathWasModified();
  3884. }
  3885. // Clear the state in the path. There is only one failure path following
  3886. // this and it leaves the path in an invalid state anyway, so we can do
  3887. // Reset here. Reset ensures that the cache is invalidated and the
  3888. // UID is recomputed.
  3889. Reset();
  3890. // Swap the path data pointers - free the old ones.
  3891. if(Ok != Points.ReplaceWith((DynArray<GpPointF> *) (&path->Points)) ||
  3892. Ok != Types.ReplaceWith((DynArray<BYTE> *) (&path->Types)))
  3893. {
  3894. // If the final commit fails due to low memory, we are hosed,
  3895. // we wiped out some of our path data and can't convert from the
  3896. // local copy, so set our status to invalid and fail the call.
  3897. SetValid(FALSE);
  3898. status = OutOfMemory;
  3899. goto Done;
  3900. }
  3901. // Need to remember the subpath count.
  3902. SubpathCount = path->SubpathCount;
  3903. Done:
  3904. delete path;
  3905. return status;
  3906. }
  3907. VOID DpPath::InitDefaultState(GpFillMode fillMode)
  3908. {
  3909. HasBezier = FALSE;
  3910. FillMode = fillMode;
  3911. Flags = PossiblyNonConvex;
  3912. IsSubpathActive = FALSE;
  3913. SubpathCount = 0;
  3914. Types.Reset(FALSE); // FALSE - don't free the memory
  3915. Points.Reset(FALSE); // FALSE - don't free the memory
  3916. SetValid(TRUE);
  3917. UpdateUid();
  3918. }
  3919. DpPath::DpPath(const DpPath* path)
  3920. {
  3921. if(path)
  3922. {
  3923. HasBezier = path->HasBezier;
  3924. FillMode = path->FillMode;
  3925. Flags = path->Flags;
  3926. IsSubpathActive = path->IsSubpathActive;
  3927. SubpathCount = path->SubpathCount;
  3928. BYTE *types = path->Types.GetDataBuffer();
  3929. GpPointF* points = path->Points.GetDataBuffer();
  3930. INT count = path->GetPointCount();
  3931. SetValid((count == 0) || ((Types.AddMultiple(types, count) == Ok) &&
  3932. (Points.AddMultiple(points, count) == Ok)));
  3933. }
  3934. else
  3935. SetValid(FALSE);
  3936. }
  3937. /**************************************************************************\
  3938. *
  3939. * Function Description:
  3940. *
  3941. * Offset all path points by the specified amount
  3942. *
  3943. * Arguments:
  3944. *
  3945. * dx, dy - Amount to offset along x- and y- direction
  3946. *
  3947. * Return Value:
  3948. *
  3949. * NONE
  3950. *
  3951. \**************************************************************************/
  3952. VOID
  3953. DpPath::Offset(
  3954. REAL dx,
  3955. REAL dy
  3956. )
  3957. {
  3958. ASSERT(IsValid());
  3959. INT count = GetPointCount();
  3960. GpPointF* pts = Points.GetDataBuffer();
  3961. if (count > 0)
  3962. {
  3963. UpdateUid();
  3964. }
  3965. while (count--)
  3966. {
  3967. pts->X += dx;
  3968. pts->Y += dy;
  3969. pts++;
  3970. }
  3971. }
  3972. /**************************************************************************\
  3973. *
  3974. * Function Description:
  3975. *
  3976. * Create a driver DpPath class.
  3977. *
  3978. * Arguments:
  3979. *
  3980. * [IN] fillMode - Specify the path fill mode
  3981. *
  3982. * Return Value:
  3983. *
  3984. * IsValid() is FALSE if failure.
  3985. *
  3986. * History:
  3987. *
  3988. * 12/08/1998 andrewgo
  3989. * Created it.
  3990. *
  3991. \**************************************************************************/
  3992. DpPath::DpPath(
  3993. const GpPointF *points,
  3994. INT count,
  3995. GpPointF *stackPoints,
  3996. BYTE *stackTypes,
  3997. INT stackCount,
  3998. GpFillMode fillMode,
  3999. DpPathFlags pathFlags
  4000. ) : Types(stackTypes, stackCount), Points(stackPoints, stackCount)
  4001. {
  4002. ASSERT((fillMode == FillModeAlternate) ||
  4003. (fillMode == FillModeWinding));
  4004. InitDefaultState(fillMode);
  4005. Flags = pathFlags;
  4006. // We can call this method with no points, just to set up
  4007. // the stackPoints/stackTypes
  4008. if (count > 0)
  4009. {
  4010. BYTE *types;
  4011. if ((types = Types.AddMultiple(count)) != NULL)
  4012. {
  4013. *types++ = PathPointTypeStart;
  4014. GpMemset(types, PathPointTypeLine, count - 1);
  4015. SetValid(Points.AddMultiple(points, count) == Ok);
  4016. if(IsValid())
  4017. {
  4018. IsSubpathActive = TRUE;
  4019. SubpathCount = 1;
  4020. }
  4021. }
  4022. else
  4023. {
  4024. SetValid(FALSE);
  4025. }
  4026. }
  4027. }
  4028. /**************************************************************************\
  4029. *
  4030. * Function Description:
  4031. *
  4032. * Close the currently active subpath in a path object
  4033. *
  4034. * Arguments:
  4035. *
  4036. * NONE
  4037. *
  4038. * Return Value:
  4039. *
  4040. * Status code
  4041. *
  4042. * History:
  4043. *
  4044. * 01/15/1999 ikkof
  4045. * Created it.
  4046. *
  4047. \**************************************************************************/
  4048. GpStatus
  4049. DpPath::CloseFigure()
  4050. {
  4051. ASSERT(IsValid());
  4052. // Check if there is an active subpath
  4053. if (IsSubpathActive)
  4054. {
  4055. // If so, mark the last point as the end of a subpath
  4056. Types.Last() |= PathPointTypeCloseSubpath;
  4057. StartFigure();
  4058. }
  4059. return Ok;
  4060. }
  4061. /**************************************************************************\
  4062. *
  4063. * Function Description:
  4064. *
  4065. * Close all open subpaths in a path object
  4066. *
  4067. * Arguments:
  4068. *
  4069. * NONE
  4070. *
  4071. * Return Value:
  4072. *
  4073. * Status code
  4074. *
  4075. * History:
  4076. *
  4077. * 01/15/1999 ikkof
  4078. * Created it.
  4079. *
  4080. \**************************************************************************/
  4081. GpStatus
  4082. DpPath::CloseFigures()
  4083. {
  4084. ASSERT(IsValid());
  4085. // Go through all path points.
  4086. // Notice that the loop index starts from 1 below.
  4087. INT i, count = GetPointCount();
  4088. BYTE* types = Types.GetDataBuffer();
  4089. for (i=1; i < count; i++)
  4090. {
  4091. if (types[i] == PathPointTypeStart)
  4092. types[i-1] |= PathPointTypeCloseSubpath;
  4093. }
  4094. if (count > 1)
  4095. types[count-1] |= PathPointTypeCloseSubpath;
  4096. StartFigure();
  4097. return Ok;
  4098. }
  4099. /**************************************************************************\
  4100. *
  4101. * Function Description:
  4102. *
  4103. * Calculates the bounds of a path
  4104. *
  4105. * Arguments:
  4106. *
  4107. * [OUT] bounds - Specify the place to stick the bounds
  4108. * [IN] matrix - Matrix used to transform the bounds
  4109. * [IN] pen - the pen data.
  4110. * [IN] dpiX, dpiY - the resolution of x and y directions.
  4111. *
  4112. * Return Value:
  4113. *
  4114. * NONE
  4115. *
  4116. * History:
  4117. *
  4118. * 12/08/1998 andrewgo
  4119. * Created it.
  4120. *
  4121. \**************************************************************************/
  4122. GpStatus
  4123. GpPath::GetBounds(
  4124. GpRect *bounds,
  4125. const GpMatrix *matrix,
  4126. const DpPen* pen,
  4127. REAL dpiX,
  4128. REAL dpiY
  4129. ) const
  4130. {
  4131. if(bounds == NULL)
  4132. return InvalidParameter;
  4133. GpRectF boundsF;
  4134. FPUStateSaver fpuState;
  4135. GpStatus status = GetBounds(&boundsF, matrix, pen, dpiX, dpiY);
  4136. if(status == Ok)
  4137. status = BoundsFToRect(&boundsF, bounds);
  4138. return status;
  4139. }
  4140. VOID
  4141. GpPath::CalcCacheBounds() const
  4142. {
  4143. INT count = GetPointCount();
  4144. GpPointF *point = Points.GetDataBuffer();
  4145. if(count <= 1)
  4146. {
  4147. ResetCacheBounds();
  4148. return;
  4149. }
  4150. REAL left, right, top, bottom;
  4151. left = point->X;
  4152. right = left;
  4153. top = point->Y;
  4154. bottom = top;
  4155. INT i;
  4156. for (i = 1, point++; i < count; i++, point++)
  4157. {
  4158. if (point->X < left)
  4159. {
  4160. left = point->X;
  4161. }
  4162. else if (point->X > right)
  4163. {
  4164. right = point->X;
  4165. }
  4166. if (point->Y < top)
  4167. {
  4168. top = point->Y;
  4169. }
  4170. else if (point->Y > bottom)
  4171. {
  4172. bottom = point->Y;
  4173. }
  4174. }
  4175. CacheBounds.X = left;
  4176. CacheBounds.Width = right - left;
  4177. CacheBounds.Y = top;
  4178. CacheBounds.Height = bottom - top;
  4179. if(CacheBounds.Width < POINTF_EPSILON && CacheBounds.Height < POINTF_EPSILON)
  4180. {
  4181. ResetCacheBounds();
  4182. return;
  4183. }
  4184. CacheFlags = kCacheBoundsValid;
  4185. }
  4186. /**************************************************************************\
  4187. *
  4188. * Function Description:
  4189. *
  4190. * Calculates the sharpest angle in a path.
  4191. *
  4192. * Arguments:
  4193. *
  4194. * NONE
  4195. *
  4196. * Return Value:
  4197. *
  4198. * NONE
  4199. *
  4200. * History:
  4201. *
  4202. * 10/04/2000 asecchia
  4203. * Created it.
  4204. *
  4205. * Remarks:
  4206. *
  4207. * This is an expensive function, if it's ever used in a performance
  4208. * critical scenario it should be recoded to use the dot product of the
  4209. * segments and perform the angle comparison in the cosine domain.
  4210. * The cost of normalizing the vectors should be cheaper than the
  4211. * atan algorithm used below.
  4212. *
  4213. *
  4214. \**************************************************************************/
  4215. VOID
  4216. GpPath::CalcSharpestAngle() const
  4217. {
  4218. if(CacheFlags & kSharpestAngleValid)
  4219. {
  4220. return;
  4221. }
  4222. UpdateCacheBounds();
  4223. // Walk the path and find the smallest angle between two
  4224. // adjacent segments.
  4225. GpPathPointIterator pIter(
  4226. (GpPointF*)GetPathPoints(),
  4227. (BYTE*)GetPathTypes(),
  4228. GetPointCount()
  4229. );
  4230. GpSubpathIterator pSubpath(&pIter);
  4231. GpPointF *points;
  4232. BOOL isClosed;
  4233. GpPointF *p0, *p1;
  4234. GpVector2D v;
  4235. REAL lastAngle;
  4236. REAL currAngle;
  4237. REAL minAngle = 2*PI;
  4238. REAL tempAngle;
  4239. bool first = true;
  4240. INT iter, i;
  4241. while(!pSubpath.IsDone())
  4242. {
  4243. // Compute the length of the subpath.
  4244. INT startIndex = pSubpath.CurrentIndex();
  4245. points = pSubpath.CurrentItem();
  4246. pSubpath.Next();
  4247. INT elementCount = pSubpath.CurrentIndex() - startIndex;
  4248. // Work out if it's a closed subpath.
  4249. // Leave the subpath iterator in the same state.
  4250. pIter.Prev();
  4251. isClosed = (*(pIter.CurrentType()) & PathPointTypeCloseSubpath) ==
  4252. PathPointTypeCloseSubpath;
  4253. pIter.Next();
  4254. // Create a GpPointF iterator.
  4255. GpArrayIterator<GpPointF> iSubpath(points, elementCount);
  4256. GpCircularIterator<GpPointF> iCirc(&iSubpath);
  4257. // Initialize the first point.
  4258. p0 = iCirc.CurrentItem();
  4259. iCirc.Next();
  4260. iter = elementCount;
  4261. first = true;
  4262. // include the endpoint wrap if it's closed
  4263. if(isClosed)
  4264. {
  4265. iter += 2;
  4266. }
  4267. for(i = 1; i < iter; i++)
  4268. {
  4269. // Get the current point.
  4270. p1 = iCirc.CurrentItem();
  4271. // Translate to the origin and compute the angle between this line
  4272. // and the x axis.
  4273. // atan2 returns values in the -PI..PI range.
  4274. v = (*p1)-(*p0);
  4275. currAngle = (REAL)atan2(v.Y, v.X);
  4276. // If we have enough data to do an angle computation, work it out.
  4277. // We require two line segments to do a computation (3 end points).
  4278. // If it's closed, we'll loop around the subpath past the beginning
  4279. // again in order to get the right amount of points.
  4280. if( !first )
  4281. {
  4282. // reverse the direction of the last segment by adding PI and
  4283. // compute the difference.
  4284. tempAngle = lastAngle + PI; // range 0 .. 2PI
  4285. // Clamp back to the -PI..PI range
  4286. if(tempAngle > PI)
  4287. {
  4288. tempAngle -= 2*PI;
  4289. }
  4290. // Difference
  4291. tempAngle = currAngle - tempAngle;
  4292. // Clamp back to the -PI..PI range
  4293. // Note that the extremes are tempAngle either -2PI or 2PI
  4294. if(tempAngle > PI)
  4295. {
  4296. tempAngle -= 2*PI;
  4297. }
  4298. if(tempAngle < -PI)
  4299. {
  4300. tempAngle += 2*PI;
  4301. }
  4302. // new minimum angle?
  4303. // We care about angle magnitude - not sign.
  4304. if( minAngle > REALABS(tempAngle) )
  4305. {
  4306. minAngle = REALABS(tempAngle);
  4307. }
  4308. }
  4309. // iterate
  4310. first = false;
  4311. lastAngle = currAngle;
  4312. iCirc.Next();
  4313. p0 = p1;
  4314. }
  4315. }
  4316. SharpestAngle = minAngle;
  4317. CacheFlags |= kSharpestAngleValid;
  4318. }
  4319. GpStatus
  4320. GpPath::GetBounds(
  4321. GpRectF *bounds, // Resulting bounds in device-space
  4322. const GpMatrix *matrix,
  4323. const DpPen* pen,
  4324. REAL dpiX,
  4325. REAL dpiY
  4326. ) const
  4327. {
  4328. if(bounds == NULL)
  4329. return InvalidParameter;
  4330. ASSERT(IsValid());
  4331. if ((dpiX <= 0) || (dpiY <= 0))
  4332. {
  4333. dpiX = Globals::DesktopDpiX;
  4334. dpiY = Globals::DesktopDpiY;
  4335. }
  4336. INT count = GetPointCount();
  4337. GpPointF *point = Points.GetDataBuffer();
  4338. if ((count == 0) || (point == NULL))
  4339. {
  4340. bounds->X = 0;
  4341. bounds->Y = 0;
  4342. bounds->Width = 0;
  4343. bounds->Height = 0;
  4344. }
  4345. else
  4346. {
  4347. REAL left, right, top, bottom;
  4348. UpdateCacheBounds();
  4349. left = CacheBounds.X;
  4350. right = left + CacheBounds.Width;
  4351. top = CacheBounds.Y;
  4352. bottom = top + CacheBounds.Height;
  4353. TransformBounds(matrix, left, top, right, bottom, bounds);
  4354. if(pen)
  4355. {
  4356. BOOL needsJoinDelta = TRUE;
  4357. if(count <= 2)
  4358. needsJoinDelta = FALSE;
  4359. GpPen* gpPen = GpPen::GetPen(pen);
  4360. // takes into account the cap width AND the pen width - JBronsk
  4361. REAL delta = gpPen->GetMaximumCapWidth(matrix, dpiX, dpiY);
  4362. if(needsJoinDelta)
  4363. {
  4364. // Since the join might be a miter type, we need to provide the
  4365. // sharpest angle in the path to see how big the join will be.
  4366. // We have the method GetSharpestAngle() that figues this out.
  4367. // But, this is really expensive since you have to iterate over
  4368. // all the points and do some trig. So, lets assume the worst
  4369. // case, which is a really sharp angle (0 rad).
  4370. const REAL sharpestAngle = 0.0f;
  4371. REAL delta1 = gpPen->GetMaximumJoinWidth(
  4372. sharpestAngle, matrix, dpiX, dpiY);
  4373. if(delta1 > delta)
  4374. delta = delta1;
  4375. }
  4376. // Only pad the bounds if there is something non-zero to pad
  4377. if (bounds->Width > REAL_EPSILON ||
  4378. bounds->Height > REAL_EPSILON)
  4379. {
  4380. bounds->X -= delta;
  4381. bounds->Y -= delta;
  4382. bounds->Width += 2*delta;
  4383. bounds->Height += 2*delta;
  4384. }
  4385. }
  4386. }
  4387. return Ok;
  4388. }
  4389. /*************************************************\
  4390. * AddGlyphPath
  4391. * History:
  4392. *
  4393. * Sept/23/1999 Xudong Wu [tessiew]
  4394. * Created it.
  4395. *
  4396. \************************************************/
  4397. GpStatus
  4398. GpPath::AddGlyphPath(
  4399. GpGlyphPath* glyphPath,
  4400. REAL x,
  4401. REAL y,
  4402. const GpMatrix * matrix
  4403. )
  4404. {
  4405. ASSERT(IsValid());
  4406. ASSERT(glyphPath->IsValid());
  4407. if (!IsValid() || !glyphPath->IsValid())
  4408. return InvalidParameter;
  4409. INT count = glyphPath->pointCount;
  4410. if (count == 0) // nothing to add
  4411. return Ok;
  4412. GpPointF* points = (GpPointF*) glyphPath->points;
  4413. BYTE* types = glyphPath->types;
  4414. if (glyphPath->hasBezier)
  4415. HasBezier = TRUE;
  4416. INT origCount = GetPointCount();
  4417. GpPointF* pointbuf = Points.AddMultiple(count);
  4418. BYTE* typebuf = Types.AddMultiple(count);
  4419. if (!pointbuf || !typebuf)
  4420. {
  4421. Points.SetCount(origCount);
  4422. Types.SetCount(origCount);
  4423. return OutOfMemory;
  4424. }
  4425. // apply the font xform
  4426. for (INT i = 0; i < count; i++)
  4427. {
  4428. pointbuf[i] = points[i];
  4429. if (matrix)
  4430. matrix->Transform(pointbuf + i);
  4431. pointbuf[i].X += x;
  4432. pointbuf[i].Y += y;
  4433. }
  4434. GpMemcpy(typebuf, types, count*sizeof(BYTE));
  4435. SubpathCount += glyphPath->curveCount;
  4436. UpdateUid();
  4437. InvalidateCache();
  4438. return Ok;
  4439. }
  4440. /*************************************************\
  4441. * AddString()
  4442. * History:
  4443. *
  4444. * 19th Oct 199 dbrown created
  4445. *
  4446. \************************************************/
  4447. GpStatus
  4448. GpPath::AddString(
  4449. const WCHAR *string,
  4450. INT length,
  4451. const GpFontFamily *family,
  4452. INT style,
  4453. REAL emSize,
  4454. const RectF *layoutRect,
  4455. const GpStringFormat *format
  4456. )
  4457. {
  4458. FPUStateSaver fpuState; // Guarantee initialised FP context
  4459. ASSERT(string && family && layoutRect);
  4460. GpStatus status;
  4461. GpTextImager *imager;
  4462. status = newTextImager(
  4463. string,
  4464. length,
  4465. layoutRect->Width,
  4466. layoutRect->Height,
  4467. family,
  4468. style,
  4469. emSize,
  4470. format,
  4471. NULL,
  4472. &imager,
  4473. TRUE // Allow use of simple text imager
  4474. );
  4475. if (status != Ok)
  4476. {
  4477. return status;
  4478. }
  4479. status = imager->AddToPath(this, &PointF(layoutRect->X, layoutRect->Y));
  4480. delete imager;
  4481. UpdateUid();
  4482. InvalidateCache();
  4483. return status;
  4484. }
  4485. // !!! why not convert to a DpRegion and convert it to a path the same way
  4486. // as the constructor that takes a DpRegion?
  4487. GpPath::GpPath(HRGN hRgn)
  4488. {
  4489. ASSERT((hRgn != NULL) && (GetObjectTypeInternal(hRgn) == OBJ_REGION));
  4490. InitDefaultState(FillModeWinding);
  4491. ASSERT(IsValid());
  4492. BYTE stackBuffer[1024];
  4493. // If our stack buffer is big enough, get the clipping contents
  4494. // in one gulp:
  4495. RGNDATA *regionBuffer = (RGNDATA*)&stackBuffer[0];
  4496. INT newSize = ::GetRegionData(hRgn, sizeof(stackBuffer), regionBuffer);
  4497. // The spec says that GetRegionData returns '1' in the event of
  4498. // success, but NT returns the actual number of bytes written if
  4499. // successful, and returns '0' if the buffer wasn't large enough:
  4500. if ((newSize < 1) || (newSize > sizeof(stackBuffer)))
  4501. {
  4502. // Our stack buffer wasn't big enough. Figure out the required
  4503. // size:
  4504. newSize = ::GetRegionData(hRgn, 0, NULL);
  4505. if (newSize > 1)
  4506. {
  4507. regionBuffer = (RGNDATA*)GpMalloc(newSize);
  4508. if (regionBuffer == NULL)
  4509. {
  4510. SetValid(FALSE);
  4511. return;
  4512. }
  4513. // Initialize to a decent result in the unlikely event of
  4514. // failure of GetRegionData:
  4515. regionBuffer->rdh.nCount = 0;
  4516. ::GetRegionData(hRgn, newSize, regionBuffer);
  4517. }
  4518. }
  4519. // Add the rects from the region to the path
  4520. if(regionBuffer->rdh.nCount > 0)
  4521. {
  4522. if (this->AddRects((RECT*)&(regionBuffer->Buffer[0]),
  4523. regionBuffer->rdh.nCount) != Ok)
  4524. {
  4525. SetValid(FALSE);
  4526. }
  4527. }
  4528. // Free the temporary buffer if one was allocated:
  4529. if (regionBuffer != (RGNDATA*) &stackBuffer[0])
  4530. {
  4531. GpFree(regionBuffer);
  4532. }
  4533. }
  4534. // create a path from a GDI+ region
  4535. GpPath::GpPath(
  4536. const DpRegion* region
  4537. )
  4538. {
  4539. InitDefaultState(FillModeAlternate);
  4540. if (region == NULL)
  4541. {
  4542. return;
  4543. }
  4544. RegionToPath convertRegion;
  4545. DynPointArray pointsArray;
  4546. if (convertRegion.ConvertRegionToPath(region, pointsArray, Types))
  4547. {
  4548. int count;
  4549. int i;
  4550. GpPointF * realPoints;
  4551. GpPoint * points;
  4552. count = Types.GetCount();
  4553. if ((count <= 0) || (pointsArray.GetCount() != count) ||
  4554. (!ValidatePathTypes(Types.GetDataBuffer(), count, &SubpathCount, &HasBezier)))
  4555. {
  4556. goto NotValid;
  4557. }
  4558. // else it is valid
  4559. // add all the space for the count in the Points up front
  4560. realPoints = Points.AddMultiple(count);
  4561. if (realPoints == NULL)
  4562. {
  4563. goto NotValid;
  4564. }
  4565. // add the points, converting from int to real
  4566. points = pointsArray.GetDataBuffer();
  4567. i = 0;
  4568. do
  4569. {
  4570. realPoints[i].X = (REAL)points[i].X;
  4571. realPoints[i].Y = (REAL)points[i].Y;
  4572. } while (++i < count);
  4573. SetValid(TRUE);
  4574. // Make sure the first point is the start type.
  4575. ASSERT(Types[0] == PathPointTypeStart);
  4576. return;
  4577. }
  4578. NotValid:
  4579. WARNING(("Failed to convert a region to a path"));
  4580. this->Reset();
  4581. SetValid(FALSE);
  4582. }
  4583. /**************************************************************************\
  4584. *
  4585. * Function Description:
  4586. *
  4587. * Returns a const pointer to the internal SubpathInfoCache. This structure
  4588. * holds the data representing the position and size of each subpath in
  4589. * the path data structures.
  4590. *
  4591. * History:
  4592. *
  4593. * 10/20/2000 asecchia
  4594. * Created.
  4595. *
  4596. \**************************************************************************/
  4597. DynArray<GpPath::SubpathInfo> *GpPath::GetSubpathInformation() const
  4598. {
  4599. if((CacheFlags & kSubpathInfoValid) == 0)
  4600. {
  4601. ComputeSubpathInformationCache();
  4602. ASSERT((CacheFlags & kSubpathInfoValid) == kSubpathInfoValid)
  4603. }
  4604. return &SubpathInfoCache;
  4605. }
  4606. /**************************************************************************\
  4607. *
  4608. * Function Description:
  4609. *
  4610. * Computes the Subpath information cache and marks it as valid.
  4611. * This code walks the entire path and stores the start and count for
  4612. * each subpath. It also notes if the subpath is closed or open.
  4613. *
  4614. * History:
  4615. *
  4616. * 10/20/2000 asecchia
  4617. * Created.
  4618. *
  4619. \**************************************************************************/
  4620. VOID GpPath::ComputeSubpathInformationCache() const
  4621. {
  4622. // Get the path data:
  4623. GpPointF *points = Points.GetDataBuffer();
  4624. BYTE *types = Types.GetDataBuffer();
  4625. INT count = Points.GetCount();
  4626. // Clear out any old cached subpath state.
  4627. SubpathInfoCache.Reset();
  4628. INT i = 0; // current position in the path.
  4629. INT c = 0; // current count of the current subpath.
  4630. // <= so that we can implicitly handle the last subpath without
  4631. // duplicating the code for the inner loop.
  4632. while(i <= count)
  4633. {
  4634. // i==count means we hit the end - and potentially need to look at
  4635. // the last subpath. Otherwise look at the most recent subpath if
  4636. // we find a new start marker.
  4637. if( ((i==count) || IsStartType(types[i])) && (i != 0))
  4638. {
  4639. // Found a subpath.
  4640. SubpathInfo subpathInfo;
  4641. subpathInfo.StartIndex = i-c;
  4642. subpathInfo.Count = c;
  4643. subpathInfo.IsClosed = IsClosedType(types[i-1]);
  4644. SubpathInfoCache.Add(subpathInfo);
  4645. // We're actually on the first point of the next subpath.
  4646. // (or we're about to terminate the loop)
  4647. c = 1;
  4648. }
  4649. else
  4650. {
  4651. c++;
  4652. }
  4653. i++;
  4654. }
  4655. // Mark the subpath information cache as valid.
  4656. CacheFlags |= kSubpathInfoValid;
  4657. }