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.

2146 lines
53 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1998 Microsoft Corporation
  4. *
  5. * Abstract:
  6. *
  7. * Implementation of GpPen class
  8. *
  9. * Revision History:
  10. *
  11. * 12/08/1998 andrewgo
  12. * Initial placeholders.
  13. *
  14. * 01/06/1999 ikkof
  15. * Added the implementation of GpGeometricPen.
  16. \**************************************************************************/
  17. #include "precomp.hpp"
  18. //-------------------------------------------------------------
  19. // GetMajorAndMinorAxis() is defined in PathWidener.cpp.
  20. //-------------------------------------------------------------
  21. extern GpStatus
  22. GetMajorAndMinorAxis(
  23. REAL* majorR,
  24. REAL* minorR,
  25. const GpMatrix* matrix
  26. );
  27. /**************************************************************************\
  28. *
  29. * Function Description:
  30. *
  31. * This converts the given width with the given physical unit to
  32. * the device unit. You cannot use this function when
  33. * unit is WorldUnit.
  34. *
  35. * Arguments:
  36. *
  37. * [IN] width - the width in the given unit.
  38. * [IN] unit - the unit of the width (must not be WorldUnit).
  39. * [IN] dpi - dots per inch of the device.
  40. *
  41. * Return Value:
  42. *
  43. * The device width.
  44. *
  45. * 04/15/1999 ikkof
  46. * Created it.
  47. *
  48. \**************************************************************************/
  49. VOID GpPen::Set(const GpColor& color, REAL penWidth, GpUnit unit)
  50. {
  51. // UnitDisplay is device-dependent and cannot be used for a pen size
  52. ASSERT(unit != UnitDisplay);
  53. if(DevicePen.CustomStartCap)
  54. delete DevicePen.CustomStartCap;
  55. if(DevicePen.CustomEndCap)
  56. delete DevicePen.CustomEndCap;
  57. if(DevicePen.DashArray)
  58. GpFree(DevicePen.DashArray);
  59. if(DevicePen.CompoundArray)
  60. GpFree(DevicePen.CompoundArray);
  61. InitDefaultState(penWidth, unit);
  62. if(Brush)
  63. {
  64. SetColor((GpColor *) &color);
  65. }
  66. else
  67. {
  68. Brush = new GpSolidFill(color);
  69. if(Brush)
  70. {
  71. DevicePen.Brush = Brush->GetDeviceBrush();
  72. }
  73. else
  74. {
  75. SetValid(FALSE);
  76. }
  77. }
  78. UpdateUid();
  79. }
  80. GpPen::GpPen(const GpColor& color, REAL penWidth, GpUnit unit)
  81. {
  82. // UnitDisplay is device-dependent and cannot be used for a pen size
  83. ASSERT(unit != UnitDisplay);
  84. InitDefaultState(penWidth, unit);
  85. Brush = new GpSolidFill(color);
  86. if(Brush)
  87. {
  88. DevicePen.Brush = Brush->GetDeviceBrush();
  89. }
  90. else
  91. {
  92. SetValid(FALSE);
  93. }
  94. }
  95. GpPen::GpPen(const GpBrush* brush, REAL penWidth, GpUnit unit)
  96. {
  97. // UnitDisplay is device-dependent and cannot be used for a pen size
  98. ASSERT(unit != UnitDisplay);
  99. InitDefaultState(penWidth, unit);
  100. Brush = brush->Clone();
  101. if(Brush)
  102. {
  103. DevicePen.Brush = Brush->GetDeviceBrush();
  104. }
  105. else
  106. {
  107. SetValid(FALSE);
  108. }
  109. }
  110. GpPen::GpPen(GpLineTexture* lineTexture, REAL penWidth, GpUnit unit)
  111. {
  112. // UnitDisplay is device-dependent and cannot be used for a pen size
  113. ASSERT(unit != UnitDisplay);
  114. // !!! Needs to be implemented.
  115. // !!! Remember to change GdipCreatePen3 - it currently just returns
  116. // NotImplemented.
  117. RIP(("GpPen with line texture not implemented"));
  118. SetValid(FALSE);
  119. }
  120. VOID GpPen::InitDefaultState(REAL penWidth, GpUnit unit)
  121. {
  122. // UnitDisplay is device-dependent and cannot be used for a pen size
  123. ASSERT(unit != UnitDisplay);
  124. // !! Look at DeviceBrush.Type
  125. DevicePen.Type = PenTypeSolidColor;
  126. DevicePen.Width = penWidth;
  127. DevicePen.Unit = unit;
  128. DevicePen.StartCap = LineCapFlat;
  129. DevicePen.EndCap = LineCapFlat;
  130. DevicePen.Join = LineJoinMiter;
  131. DevicePen.MiterLimit = 10; // PS's default miter limit.
  132. DevicePen.PenAlignment = PenAlignmentCenter;
  133. DevicePen.DashStyle = DashStyleSolid;
  134. DevicePen.DashCap = LineCapFlat;
  135. DevicePen.DashCount = 0;
  136. DevicePen.DashOffset = 0;
  137. DevicePen.DashArray = NULL;
  138. DevicePen.CompoundCount = 0;
  139. DevicePen.CompoundArray = NULL;
  140. DevicePen.CustomStartCap = NULL;
  141. DevicePen.CustomEndCap = NULL;
  142. DevicePen.Xform.Reset();
  143. SetValid(TRUE);
  144. UpdateUid();
  145. }
  146. GpPen::GpPen(const GpPen* pen)
  147. {
  148. GpStatus status = Ok;
  149. // Initialize pointer members so that we don't delete garbage
  150. Brush = NULL;
  151. DevicePen.Brush = NULL;
  152. DevicePen.DashArray = NULL;
  153. DevicePen.CompoundArray = NULL;
  154. DevicePen.CustomStartCap = NULL;
  155. DevicePen.CustomEndCap = NULL;
  156. if(pen && pen->IsValid())
  157. {
  158. // Copy the base state.
  159. DevicePen = pen->DevicePen;
  160. // Don't copy pointer references to other objects.
  161. Brush = NULL;
  162. DevicePen.Brush = NULL;
  163. DevicePen.DashArray = NULL;
  164. DevicePen.CompoundArray = NULL;
  165. DevicePen.CustomStartCap = NULL;
  166. DevicePen.CustomEndCap = NULL;
  167. // Explicitly clone the pointer references to other objects.
  168. if(pen->Brush)
  169. {
  170. Brush = pen->Brush->Clone();
  171. DevicePen.Brush = Brush->GetDeviceBrush();
  172. }
  173. else
  174. {
  175. status = GenericError;
  176. }
  177. if( status == Ok )
  178. {
  179. if( (pen->DevicePen.DashArray) &&
  180. (DevicePen.DashCount > 0)
  181. )
  182. {
  183. DevicePen.DashArray = (REAL*) GpMalloc(DevicePen.DashCount*sizeof(REAL));
  184. if(DevicePen.DashArray)
  185. {
  186. GpMemcpy(DevicePen.DashArray, pen->DevicePen.DashArray, DevicePen.DashCount*sizeof(REAL));
  187. }
  188. else
  189. {
  190. status = OutOfMemory;
  191. }
  192. }
  193. else
  194. {
  195. // If there is no dash array data, this must be a solid line.
  196. ASSERT(DevicePen.DashStyle == DashStyleSolid);
  197. DevicePen.DashCount = 0;
  198. DevicePen.DashArray = NULL;
  199. }
  200. }
  201. // Set the compound array if necessary.
  202. if( status == Ok )
  203. {
  204. if( (pen->DevicePen.CompoundArray) &&
  205. (DevicePen.CompoundCount > 0)
  206. )
  207. {
  208. DevicePen.CompoundArray = (REAL*) GpMalloc(DevicePen.CompoundCount*sizeof(REAL));
  209. if(DevicePen.CompoundArray)
  210. {
  211. GpMemcpy(DevicePen.CompoundArray, pen->DevicePen.CompoundArray, DevicePen.CompoundCount*sizeof(REAL));
  212. }
  213. else
  214. {
  215. status = OutOfMemory;
  216. }
  217. }
  218. else
  219. {
  220. DevicePen.CompoundCount = 0;
  221. DevicePen.CompoundArray = NULL;
  222. }
  223. }
  224. // Copy the start custom cap.
  225. if( status == Ok )
  226. {
  227. if( DevicePen.StartCap == LineCapCustom )
  228. {
  229. // This could happen with our metafile recorder,
  230. // because saving Custom Line Caps was not implemented.
  231. if (pen->DevicePen.CustomStartCap == NULL)
  232. {
  233. WARNING1("CustomStartCap type with NULL pointer");
  234. DevicePen.StartCap = LineCapFlat;
  235. }
  236. else
  237. {
  238. GpCustomLineCap* clonedCap = static_cast<GpCustomLineCap*>
  239. (pen->DevicePen.CustomStartCap)->Clone();
  240. if(clonedCap)
  241. {
  242. DevicePen.CustomStartCap = clonedCap;
  243. }
  244. else
  245. {
  246. status = OutOfMemory;
  247. }
  248. }
  249. }
  250. }
  251. // Copy the end custom cap.
  252. if( status == Ok )
  253. {
  254. if( DevicePen.EndCap == LineCapCustom )
  255. {
  256. // This could happen with our metafile recorder,
  257. // because saving Custom Line Caps was not implemented.
  258. if (pen->DevicePen.CustomEndCap == NULL)
  259. {
  260. WARNING1("CustomEndCap type with NULL pointer");
  261. DevicePen.EndCap = LineCapFlat;
  262. }
  263. else
  264. {
  265. GpCustomLineCap* clonedCap = static_cast<GpCustomLineCap*>
  266. (pen->DevicePen.CustomEndCap)->Clone();
  267. if(clonedCap)
  268. {
  269. DevicePen.CustomEndCap = clonedCap;
  270. }
  271. else
  272. {
  273. status = OutOfMemory;
  274. }
  275. }
  276. }
  277. }
  278. }
  279. else
  280. {
  281. // Can't make a valid pen from an invalid input pen.
  282. status = GenericError;
  283. }
  284. if(status == Ok)
  285. {
  286. SetValid(TRUE);
  287. }
  288. else
  289. {
  290. // Failed cloning the pen.
  291. // Clean up possible memory allocation so we don't leak even under
  292. // low memory conditions. Note we rely on GpFree and delete handling
  293. // NULL pointers here.
  294. delete Brush;
  295. Brush = NULL; // InitializeDefaultState() does not set
  296. DevicePen.Brush = NULL; // these fields - clear them explicitly.
  297. GpFree(DevicePen.DashArray);
  298. GpFree(DevicePen.CompoundArray);
  299. delete DevicePen.CustomStartCap;
  300. delete DevicePen.CustomEndCap;
  301. // Clean the pen.
  302. InitDefaultState(1.0f, UnitWorld);
  303. // This is not a valid object.
  304. SetValid(FALSE);
  305. }
  306. }
  307. // Clone() return NULL if the cloning fails.
  308. GpPen* GpPen::Clone()
  309. {
  310. GpPen* clonedPen = new GpPen(this);
  311. if(clonedPen && clonedPen->IsValid())
  312. return clonedPen;
  313. else
  314. {
  315. if(clonedPen)
  316. delete clonedPen;
  317. return NULL;
  318. }
  319. }
  320. GpStatus
  321. GpPen::GetMaximumWidth(
  322. REAL* width,
  323. const GpMatrix* matrix) const
  324. {
  325. if(DevicePen.Unit != UnitWorld)
  326. return InvalidParameter;
  327. GpMatrix trans;
  328. if(matrix)
  329. trans = *matrix;
  330. if(!DevicePen.Xform.IsTranslate())
  331. trans.Prepend(DevicePen.Xform);
  332. REAL majorR, minorR;
  333. ::GetMajorAndMinorAxis(&majorR, &minorR, &trans);
  334. majorR *= DevicePen.Width;
  335. minorR *= DevicePen.Width;
  336. if(minorR < 1.42f) // This is a litte bit larger than sqrt(2).
  337. {
  338. minorR = 1.42f;
  339. majorR = 1.42f;
  340. }
  341. *width = majorR;
  342. return Ok;
  343. }
  344. /**************************************************************************\
  345. *
  346. * Function Description:
  347. *
  348. * This function takes a join angle and computes the length of the miter
  349. * based on this angle and a given miter length limit.
  350. * This can be scaled by the pen width to give the length of an arbitrary
  351. * pen miter.
  352. *
  353. * In this picture, 2a is the angle of the join. The pen width is w and the
  354. * desired output is the length of the miter join (l).
  355. *
  356. * Note that the line labled w is perpendecular to the inside and outside
  357. * widended lines. Then the formula is derived as follows:
  358. *
  359. * sin(a) = w/l [opposite over hypotenuse on right angled triangle]
  360. * <=> l = w/sin(a)
  361. *
  362. *
  363. * /|\
  364. * /a|a\
  365. * / | \
  366. * / | \
  367. * / |l \
  368. * / | \ <-- right angle
  369. * /--__ | __--\
  370. * / w --|-- w \
  371. * / / \ \
  372. * / / \ \
  373. * outside inside outside
  374. *
  375. * NOTE:
  376. *
  377. * This routine returns the miter length (l) for a pen width w==1.0f.
  378. * The caller is responsible for scaling length by the pen width.
  379. *
  380. * If the length of 1/sin(a) is greater than the miterLimit, the miterLimit
  381. * is returned. (including infinite length joins).
  382. *
  383. * Arguments:
  384. *
  385. * [IN] angle - join angle in radians
  386. * [IN] miterLimit - maximum miter length (not scaled by pen width).
  387. *
  388. * Return Value:
  389. *
  390. * Pen width independent miter length.
  391. *
  392. * 10/02/2000 asecchia
  393. * Created it.
  394. *
  395. \**************************************************************************/
  396. REAL GpPen::ComputeMiterLength(
  397. REAL angle,
  398. REAL miterLimit
  399. )
  400. {
  401. // use the simple miter join formula
  402. // length = (penwidth)/sin(angle/2)
  403. // because we're pen independent, use 1.0 for pen width and rely
  404. // on the caller to scale by the pen width.
  405. REAL length = (REAL)sin(0.5*angle);
  406. // Check for an infinite miter...
  407. if(REALABS(length) < REAL_EPSILON)
  408. {
  409. return miterLimit;
  410. }
  411. length = 1.0f / length;
  412. return min(miterLimit, length);
  413. }
  414. REAL
  415. GpPen::GetMaximumJoinWidth(
  416. REAL sharpestAngle,
  417. const GpMatrix* matrix,
  418. REAL dpiX,
  419. REAL dpiY) const
  420. {
  421. REAL delta;
  422. if ((matrix != NULL) && (DevicePen.IsOnePixelWideSolid(matrix, dpiX)))
  423. {
  424. delta = 0.5;
  425. }
  426. else
  427. {
  428. REAL maximumWidth;
  429. REAL delta0;
  430. REAL scale = 1.0;
  431. switch(DevicePen.PenAlignment)
  432. {
  433. case PenAlignmentCenter:
  434. scale = 0.5f;
  435. break;
  436. // use 1.0 for the inset pen. If the path is open, we render with a
  437. // center pen.
  438. // NOTE: a scale of 0.0 is sufficient for all inset pen rendering
  439. // provided all subpaths are closed. In the widener, we detect the
  440. // open subpaths and render them with a center pen. To accommodate
  441. // this, we increase the scale here. Theoretically we could use
  442. // scale = 0.5f for the inset pen (same as center pen), but this
  443. // bounds is an overestimate anyway and being wrong by one pixel too
  444. // small is way worse (crash) than being wrong and too big.
  445. case PenAlignmentInset:
  446. scale = 1.0f;
  447. break;
  448. }
  449. if(GetMaximumWidth(&maximumWidth, matrix) == Ok)
  450. {
  451. delta0 = maximumWidth;
  452. }
  453. else
  454. {
  455. maximumWidth = ::GetDeviceWidth(
  456. DevicePen.Width,
  457. DevicePen.Unit,
  458. dpiX);
  459. delta0 = maximumWidth;
  460. }
  461. if(DevicePen.Join == LineJoinMiter ||
  462. DevicePen.Join == LineJoinMiterClipped)
  463. {
  464. REAL miterLimit = DevicePen.MiterLimit;
  465. delta = delta0*miterLimit;
  466. if(delta > 20)
  467. {
  468. delta = ComputeMiterLength(
  469. sharpestAngle,
  470. miterLimit
  471. );
  472. // scale by the pen width.
  473. delta *= delta0;
  474. }
  475. }
  476. else
  477. {
  478. delta = delta0;
  479. }
  480. delta *= scale;
  481. }
  482. return delta;
  483. }
  484. REAL
  485. GpPen::GetMaximumCapWidth(
  486. const GpMatrix* matrix,
  487. REAL dpiX,
  488. REAL dpiY) const
  489. {
  490. REAL maximumWidth;
  491. REAL delta0;
  492. if(GetMaximumWidth(&maximumWidth, matrix) == Ok)
  493. {
  494. delta0 = maximumWidth;
  495. }
  496. else
  497. {
  498. maximumWidth = ::GetDeviceWidth(
  499. DevicePen.Width,
  500. DevicePen.Unit,
  501. dpiX);
  502. delta0 = maximumWidth;
  503. }
  504. REAL delta = delta0;
  505. GpLineCap startCap = DevicePen.StartCap;
  506. GpLineCap endCap = DevicePen.EndCap;
  507. REAL delta1;
  508. GpCustomLineCap* customCap = NULL;
  509. if(startCap == LineCapCustom && DevicePen.CustomStartCap)
  510. {
  511. customCap = static_cast<GpCustomLineCap *> (DevicePen.CustomStartCap);
  512. delta1 = customCap->GetRadius(delta0, 1.0f);
  513. }
  514. else
  515. {
  516. if(!(startCap & LineCapAnchorMask))
  517. delta1 = 0.5f*delta0;
  518. else
  519. delta1 = 2.0f*(delta0 + 1);
  520. }
  521. if(delta < delta1)
  522. delta = delta1;
  523. if(endCap == LineCapCustom && DevicePen.CustomEndCap)
  524. {
  525. customCap = static_cast<GpCustomLineCap *> (DevicePen.CustomEndCap);
  526. delta1 = customCap->GetRadius(delta0, 1.0f);
  527. }
  528. else
  529. {
  530. if(!(endCap & LineCapAnchorMask))
  531. delta1 = 0.5f*delta0;
  532. else
  533. delta1 = 2.0f*(delta0 + 2);
  534. }
  535. if(delta < delta1)
  536. delta = delta1;
  537. return delta;
  538. }
  539. VOID
  540. GpPen::SetDashCap(GpDashCap dashCap)
  541. {
  542. // Note: Internally we use a GpLineCap type to store the dash cap type.
  543. // So we need to convert between GpLineCap and GpDashCap.
  544. // However, we should change the internal usage to GpDashCap in v2.
  545. // - JBronsk
  546. GpLineCap lineCap = LineCapFlat;
  547. switch (dashCap)
  548. {
  549. case DashCapRound:
  550. lineCap = LineCapRound;
  551. break;
  552. case DashCapTriangle:
  553. lineCap = LineCapTriangle;
  554. break;
  555. // all others map to LineCapFlat
  556. }
  557. GpStatus status = SetDashStyleWithDashCap(DevicePen.DashStyle, lineCap);
  558. if(status == Ok)
  559. {
  560. DevicePen.DashCap = lineCap;
  561. }
  562. }
  563. GpStatus
  564. GpPen::SetDashStyle(
  565. GpDashStyle dashStyle
  566. )
  567. {
  568. return SetDashStyleWithDashCap(dashStyle, DevicePen.DashCap);
  569. }
  570. GpStatus
  571. GpPen::SetDashStyleWithDashCap(
  572. GpDashStyle dashStyle,
  573. GpLineCap dashCap
  574. )
  575. {
  576. GpStatus status = Ok;
  577. REAL style[6];
  578. INT count;
  579. switch(dashStyle)
  580. {
  581. case DashStyleSolid:
  582. count = 0;
  583. break;
  584. case DashStyleDash:
  585. count = 2;
  586. style[0] = 3; // a dash
  587. style[1] = 1; // a space
  588. break;
  589. case DashStyleDot:
  590. count = 2;
  591. style[0] = 1; // a dot
  592. style[1] = 1; // a space
  593. break;
  594. case DashStyleDashDot:
  595. count = 4;
  596. style[0] = 3; // a dash
  597. style[1] = 1; // a space
  598. style[2] = 1; // a dot
  599. style[3] = 1; // a space
  600. break;
  601. case DashStyleDashDotDot:
  602. count = 6;
  603. style[0] = 3; // a dash
  604. style[1] = 1; // a space
  605. style[2] = 1; // a dot
  606. style[3] = 1; // a space
  607. style[4] = 1; // a dot
  608. style[5] = 1; // a space
  609. break;
  610. case DashStyleCustom:
  611. // We assume that the custom dash has been set at the API.
  612. // The remaining code in this routine is for initializing an appropriate
  613. // dash array, which we already have in this case, so we're done.
  614. DevicePen.DashStyle = dashStyle;
  615. return Ok;
  616. default:
  617. // The dash style must be one of the predefined ones.
  618. status = InvalidParameter;
  619. }
  620. if(status != Ok)
  621. {
  622. return status;
  623. }
  624. if(DevicePen.DashCount < count)
  625. {
  626. REAL* newArray = (REAL*) GpMalloc(count*sizeof(REAL));
  627. if(newArray)
  628. {
  629. GpFree(DevicePen.DashArray);
  630. DevicePen.DashArray = newArray;
  631. }
  632. else
  633. {
  634. status = OutOfMemory;
  635. }
  636. }
  637. if(status == Ok)
  638. {
  639. // initialize the DashArray.
  640. GpMemcpy(DevicePen.DashArray, &style[0], count*sizeof(REAL));
  641. DevicePen.DashStyle = dashStyle;
  642. DevicePen.DashCount = count;
  643. UpdateUid();
  644. }
  645. return status;
  646. }
  647. GpStatus
  648. GpPen::SetDashArray(
  649. const REAL* dashArray,
  650. INT count
  651. )
  652. {
  653. ASSERT(dashArray && count > 0);
  654. // Make sure the all elements are positive.
  655. INT i = 0;
  656. GpStatus status = Ok;
  657. while(status == Ok && i < count)
  658. {
  659. if(dashArray[i++] <= 0)
  660. status = InvalidParameter;
  661. }
  662. if(status != Ok)
  663. return status;
  664. REAL* newArray = (REAL*) GpRealloc(DevicePen.DashArray, count*sizeof(REAL));
  665. if(!newArray)
  666. return OutOfMemory;
  667. GpMemcpy(newArray, dashArray, count*sizeof(REAL));
  668. DevicePen.DashStyle = DashStyleCustom;
  669. DevicePen.DashArray = newArray;
  670. DevicePen.DashCount = count;
  671. UpdateUid();
  672. return Ok;
  673. }
  674. GpStatus
  675. GpPen::GetDashArray(
  676. REAL* dashArray,
  677. INT count
  678. ) const
  679. {
  680. ASSERT(dashArray != NULL && count <= DevicePen.DashCount);
  681. GpStatus status = Ok;
  682. if(dashArray == NULL || count > DevicePen.DashCount)
  683. return InvalidParameter;
  684. if(DevicePen.DashArray)
  685. GpMemcpy(dashArray, DevicePen.DashArray, count*sizeof(REAL));
  686. else
  687. status = OutOfMemory;
  688. return status;
  689. }
  690. GpStatus
  691. GpPen::SetCompoundArray(
  692. const REAL* compoundArray,
  693. INT count
  694. )
  695. {
  696. // count must be a positive even number.
  697. if(compoundArray == NULL || count <= 0 || (count & 0x01))
  698. {
  699. return InvalidParameter;
  700. }
  701. // count is 2 or more here...
  702. // Compound Inset pens aren't implemented yet.
  703. // The code for correctly handling minimum width compound sub lines
  704. // is missing.
  705. if(DevicePen.PenAlignment == PenAlignmentInset)
  706. {
  707. return NotImplemented;
  708. }
  709. // Make sure the all elements are monitonically increasing
  710. // and its values are between 0 and 1.
  711. GpStatus status = Ok;
  712. REAL lastValue, nextValue;
  713. lastValue = compoundArray[0];
  714. if(lastValue < 0.0f || lastValue > 1.0f)
  715. status = InvalidParameter;
  716. INT i = 1;
  717. while(status == Ok && i < count)
  718. {
  719. nextValue = compoundArray[i++];
  720. if(nextValue < lastValue || nextValue > 1.0f)
  721. status = InvalidParameter;
  722. lastValue = nextValue;
  723. }
  724. if(status != Ok)
  725. return status;
  726. REAL* newArray = (REAL*) GpRealloc(DevicePen.CompoundArray, count*sizeof(REAL));
  727. if(!newArray)
  728. return OutOfMemory;
  729. GpMemcpy(newArray, compoundArray, count*sizeof(REAL));
  730. DevicePen.CompoundArray = newArray;
  731. DevicePen.CompoundCount = count;
  732. UpdateUid();
  733. return Ok;
  734. }
  735. GpStatus
  736. GpPen::GetCompoundArray(
  737. REAL* compoundArray,
  738. INT count
  739. )
  740. {
  741. ASSERT(compoundArray != NULL && count <= DevicePen.CompoundCount);
  742. if(compoundArray == NULL || count > DevicePen.CompoundCount)
  743. return InvalidParameter;
  744. if(DevicePen.CompoundArray && count > 0)
  745. GpMemcpy(compoundArray, DevicePen.CompoundArray, count*sizeof(REAL));
  746. return Ok;
  747. }
  748. GpStatus
  749. GpPen::SetCustomStartCap(
  750. const GpCustomLineCap* customCap
  751. )
  752. {
  753. if(DevicePen.CustomStartCap)
  754. delete DevicePen.CustomStartCap;
  755. // Reset the standard start cap to the default one.
  756. DevicePen.CustomStartCap = NULL;
  757. DevicePen.StartCap = LineCapFlat;
  758. if(customCap)
  759. {
  760. DevicePen.CustomStartCap = customCap->Clone();
  761. DevicePen.StartCap = LineCapCustom;
  762. }
  763. UpdateUid();
  764. return Ok;
  765. }
  766. GpStatus
  767. GpPen::GetCustomStartCap(
  768. GpCustomLineCap** customCap
  769. )
  770. {
  771. if(DevicePen.CustomStartCap)
  772. *customCap = static_cast<GpCustomLineCap*>
  773. (DevicePen.CustomStartCap)->Clone();
  774. else
  775. *customCap = NULL;
  776. return Ok;
  777. }
  778. GpStatus
  779. GpPen::SetCustomEndCap(
  780. const GpCustomLineCap* customCap
  781. )
  782. {
  783. if(DevicePen.CustomEndCap)
  784. delete DevicePen.CustomEndCap;
  785. // Reset the standard start cap to the default one.
  786. DevicePen.CustomEndCap = NULL;
  787. DevicePen.EndCap = LineCapFlat;
  788. if(customCap)
  789. {
  790. DevicePen.CustomEndCap = customCap->Clone();
  791. DevicePen.EndCap = LineCapCustom;
  792. }
  793. UpdateUid();
  794. return Ok;
  795. }
  796. GpStatus
  797. GpPen::GetCustomEndCap(
  798. GpCustomLineCap** customCap
  799. )
  800. {
  801. if(DevicePen.CustomEndCap)
  802. *customCap = static_cast<GpCustomLineCap*>
  803. (DevicePen.CustomEndCap)->Clone();
  804. else
  805. *customCap = NULL;
  806. return Ok;
  807. }
  808. GpStatus
  809. GpPen::MultiplyTransform(const GpMatrix& matrix,
  810. GpMatrixOrder order)
  811. {
  812. GpStatus status = Ok;
  813. if (matrix.IsInvertible())
  814. {
  815. if (order == MatrixOrderPrepend)
  816. {
  817. DevicePen.Xform.Prepend(matrix);
  818. }
  819. else
  820. {
  821. DevicePen.Xform.Append(matrix);
  822. }
  823. }
  824. else
  825. status = InvalidParameter;
  826. return status;
  827. }
  828. /**************************************************************************\
  829. *
  830. * Function Description:
  831. *
  832. * Answer true if the two pen instances are equivalent, meaning they
  833. * are indistinguishable when rendering.
  834. *
  835. * Arguments:
  836. *
  837. * [IN] pen - pen to compare this against
  838. * Return Value:
  839. *
  840. * TRUE if equivalent.
  841. *
  842. * Created:
  843. *
  844. * 6/14/1999 peterost
  845. *
  846. \**************************************************************************/
  847. BOOL
  848. GpPen::IsEqual(
  849. const GpPen * pen
  850. )
  851. const
  852. {
  853. ASSERT(pen != NULL);
  854. if (pen == this)
  855. return TRUE;
  856. BOOL isEqual = TRUE;
  857. if (DevicePen.IsEqual(&pen->DevicePen) &&
  858. DevicePen.DashStyle == pen->DevicePen.DashStyle &&
  859. DevicePen.CompoundCount == pen->DevicePen.CompoundCount &&
  860. Brush->IsEqual(pen->Brush) &&
  861. DevicePen.Xform.IsEqual(&pen->DevicePen.Xform))
  862. {
  863. // We need to check the equality further if the dash style
  864. // is not a solid line.
  865. if (DevicePen.DashStyle != DashStyleSolid)
  866. {
  867. if(DevicePen.DashStyle != DashStyleCustom)
  868. {
  869. // A case of the preset dash pattern.
  870. // Check only for the offset difference.
  871. if(DevicePen.DashOffset != pen->DevicePen.DashOffset)
  872. isEqual = FALSE;
  873. }
  874. else
  875. {
  876. if (DevicePen.DashCount == pen->DevicePen.DashCount &&
  877. DevicePen.DashOffset == pen->DevicePen.DashOffset &&
  878. DevicePen.DashArray != NULL &&
  879. pen->DevicePen.DashArray != NULL)
  880. {
  881. INT i = 0;
  882. while(i < DevicePen.DashCount && isEqual)
  883. {
  884. if (DevicePen.DashArray[i] != pen->DevicePen.DashArray[i])
  885. {
  886. isEqual = FALSE;
  887. }
  888. i++;
  889. }
  890. }
  891. else
  892. {
  893. isEqual = FALSE;
  894. }
  895. }
  896. }
  897. // Check for the compound lines.
  898. if(isEqual && DevicePen.CompoundCount > 0)
  899. {
  900. if(DevicePen.CompoundArray && pen->DevicePen.CompoundArray)
  901. {
  902. INT j = 0;
  903. while(j < DevicePen.CompoundCount && isEqual)
  904. {
  905. if(DevicePen.CompoundArray[j] != pen->DevicePen.CompoundArray[j])
  906. {
  907. isEqual = FALSE;
  908. }
  909. j++;
  910. }
  911. }
  912. else
  913. {
  914. isEqual = FALSE;
  915. }
  916. }
  917. }
  918. else
  919. {
  920. isEqual = FALSE;
  921. }
  922. return isEqual;
  923. }
  924. // For GetData and SetData methods
  925. #define GDIP_PENFLAGS_TRANSFORM 0x00000001
  926. #define GDIP_PENFLAGS_STARTCAP 0x00000002
  927. #define GDIP_PENFLAGS_ENDCAP 0x00000004
  928. #define GDIP_PENFLAGS_JOIN 0x00000008
  929. #define GDIP_PENFLAGS_MITERLIMIT 0x00000010
  930. #define GDIP_PENFLAGS_DASHSTYLE 0x00000020
  931. #define GDIP_PENFLAGS_DASHCAP 0x00000040
  932. #define GDIP_PENFLAGS_DASHOFFSET 0x00000080
  933. #define GDIP_PENFLAGS_DASHARRAY 0x00000100
  934. #define GDIP_PENFLAGS_NONCENTER 0x00000200
  935. #define GDIP_PENFLAGS_COMPOUNDARRAY 0x00000400
  936. #define GDIP_PENFLAGS_CUSTOMSTARTCAP 0x00000800
  937. #define GDIP_PENFLAGS_CUSTOMENDCAP 0x00001000
  938. class PenData : public ObjectTypeData
  939. {
  940. public:
  941. INT32 Flags;
  942. INT32 Unit;
  943. REAL Width;
  944. };
  945. /**************************************************************************\
  946. *
  947. * Function Description:
  948. *
  949. * Get the pen data.
  950. *
  951. * Arguments:
  952. *
  953. * [IN] dataBuffer - fill this buffer with the data
  954. * [IN/OUT] size - IN - size of buffer; OUT - number bytes written
  955. *
  956. * Return Value:
  957. *
  958. * GpStatus - Ok or error code
  959. *
  960. * Created:
  961. *
  962. * 9/13/1999 DCurtis
  963. *
  964. \**************************************************************************/
  965. GpStatus
  966. GpPen::GetData(
  967. IStream * stream
  968. ) const
  969. {
  970. if (Brush == NULL)
  971. {
  972. WARNING(("Brush is NULL"));
  973. return Ok;
  974. }
  975. ASSERT (stream != NULL);
  976. INT flags = 0;
  977. if (!DevicePen.Xform.IsIdentity())
  978. {
  979. flags |= GDIP_PENFLAGS_TRANSFORM;
  980. }
  981. INT customStartCapSize = 0;
  982. INT customEndCapSize = 0;
  983. if (DevicePen.StartCap != LineCapFlat)
  984. {
  985. if (DevicePen.StartCap == LineCapCustom)
  986. {
  987. if ((DevicePen.CustomStartCap != NULL) &&
  988. DevicePen.CustomStartCap->IsValid() &&
  989. ((customStartCapSize = DevicePen.CustomStartCap->GetDataSize()) > 0))
  990. {
  991. flags |= GDIP_PENFLAGS_STARTCAP | GDIP_PENFLAGS_CUSTOMSTARTCAP;
  992. }
  993. }
  994. else
  995. {
  996. flags |= GDIP_PENFLAGS_STARTCAP;
  997. }
  998. }
  999. if (DevicePen.EndCap != LineCapFlat)
  1000. {
  1001. if (DevicePen.EndCap == LineCapCustom)
  1002. {
  1003. if ((DevicePen.CustomEndCap != NULL) &&
  1004. DevicePen.CustomEndCap->IsValid() &&
  1005. ((customEndCapSize = DevicePen.CustomEndCap->GetDataSize()) > 0))
  1006. {
  1007. flags |= GDIP_PENFLAGS_ENDCAP | GDIP_PENFLAGS_CUSTOMENDCAP;
  1008. }
  1009. }
  1010. else
  1011. {
  1012. flags |= GDIP_PENFLAGS_ENDCAP;
  1013. }
  1014. }
  1015. if (DevicePen.Join != LineJoinMiter)
  1016. {
  1017. flags |= GDIP_PENFLAGS_JOIN;
  1018. }
  1019. if (DevicePen.MiterLimit != 10)
  1020. {
  1021. flags |= GDIP_PENFLAGS_MITERLIMIT;
  1022. }
  1023. // DashStyleCustom is handled by hasDashArray
  1024. if ((DevicePen.DashStyle != DashStyleSolid) && (DevicePen.DashStyle != DashStyleCustom))
  1025. {
  1026. flags |= GDIP_PENFLAGS_DASHSTYLE;
  1027. }
  1028. if (DevicePen.DashCap != LineCapFlat)
  1029. {
  1030. flags |= GDIP_PENFLAGS_DASHCAP;
  1031. }
  1032. if (DevicePen.DashOffset != 0)
  1033. {
  1034. flags |= GDIP_PENFLAGS_DASHOFFSET;
  1035. }
  1036. if ((DevicePen.DashStyle == DashStyleCustom) &&
  1037. (DevicePen.DashArray != NULL) &&
  1038. (DevicePen.DashCount > 0))
  1039. {
  1040. flags |= GDIP_PENFLAGS_DASHARRAY;
  1041. }
  1042. if (DevicePen.PenAlignment != PenAlignmentCenter)
  1043. {
  1044. flags |= GDIP_PENFLAGS_NONCENTER;
  1045. }
  1046. if ((DevicePen.CompoundArray != NULL) && (DevicePen.CompoundCount > 0))
  1047. {
  1048. flags |= GDIP_PENFLAGS_COMPOUNDARRAY;
  1049. }
  1050. PenData penData;
  1051. penData.Type = DevicePen.Type;
  1052. penData.Flags = flags;
  1053. penData.Unit = DevicePen.Unit;
  1054. penData.Width = DevicePen.Width;
  1055. stream->Write(&penData, sizeof(penData), NULL);
  1056. if (flags & GDIP_PENFLAGS_TRANSFORM)
  1057. {
  1058. DevicePen.Xform.WriteMatrix(stream);
  1059. }
  1060. if (flags & GDIP_PENFLAGS_STARTCAP)
  1061. {
  1062. stream->Write(&DevicePen.StartCap, sizeof(INT32), NULL);
  1063. }
  1064. if (flags & GDIP_PENFLAGS_ENDCAP)
  1065. {
  1066. stream->Write(&DevicePen.EndCap, sizeof(INT32), NULL);
  1067. }
  1068. if (flags & GDIP_PENFLAGS_JOIN)
  1069. {
  1070. stream->Write(&DevicePen.Join, sizeof(INT32), NULL);
  1071. }
  1072. if (flags & GDIP_PENFLAGS_MITERLIMIT)
  1073. {
  1074. stream->Write(&DevicePen.MiterLimit, sizeof(REAL), NULL);
  1075. }
  1076. if (flags & GDIP_PENFLAGS_DASHSTYLE)
  1077. {
  1078. stream->Write(&DevicePen.DashStyle, sizeof(INT32), NULL);
  1079. }
  1080. if (flags & GDIP_PENFLAGS_DASHCAP)
  1081. {
  1082. stream->Write(&DevicePen.DashCap, sizeof(INT32), NULL);
  1083. }
  1084. if (flags & GDIP_PENFLAGS_DASHOFFSET)
  1085. {
  1086. stream->Write(&DevicePen.DashOffset, sizeof(REAL), NULL);
  1087. }
  1088. if (flags & GDIP_PENFLAGS_DASHARRAY)
  1089. {
  1090. stream->Write(&DevicePen.DashCount, sizeof(INT32), NULL);
  1091. stream->Write(DevicePen.DashArray, DevicePen.DashCount * sizeof(REAL), NULL);
  1092. }
  1093. if (flags & GDIP_PENFLAGS_NONCENTER)
  1094. {
  1095. stream->Write(&DevicePen.PenAlignment, sizeof(INT32), NULL);
  1096. }
  1097. if (flags & GDIP_PENFLAGS_COMPOUNDARRAY)
  1098. {
  1099. stream->Write(&DevicePen.CompoundCount, sizeof(INT32), NULL);
  1100. stream->Write(DevicePen.CompoundArray, DevicePen.CompoundCount * sizeof(REAL), NULL);
  1101. }
  1102. GpStatus status;
  1103. if (flags & GDIP_PENFLAGS_CUSTOMSTARTCAP)
  1104. {
  1105. stream->Write(&customStartCapSize, sizeof(INT32), NULL);
  1106. if ((status = DevicePen.CustomStartCap->GetData(stream)) != Ok)
  1107. {
  1108. return status;
  1109. }
  1110. }
  1111. if (flags & GDIP_PENFLAGS_CUSTOMENDCAP)
  1112. {
  1113. stream->Write(&customEndCapSize, sizeof(INT32), NULL);
  1114. if ((status = DevicePen.CustomEndCap->GetData(stream)) != Ok)
  1115. {
  1116. return status;
  1117. }
  1118. }
  1119. status = Brush->GetData(stream);
  1120. return status;
  1121. }
  1122. UINT
  1123. GpPen::GetDataSize() const
  1124. {
  1125. if (Brush == NULL)
  1126. {
  1127. WARNING(("Brush is NULL"));
  1128. return 0;
  1129. }
  1130. UINT dataSize = sizeof(PenData);
  1131. if (!DevicePen.Xform.IsIdentity())
  1132. {
  1133. dataSize += GDIP_MATRIX_SIZE;
  1134. }
  1135. INT customStartCapSize = 0;
  1136. INT customEndCapSize = 0;
  1137. if (DevicePen.StartCap != LineCapFlat)
  1138. {
  1139. if (DevicePen.StartCap == LineCapCustom)
  1140. {
  1141. if ((DevicePen.CustomStartCap != NULL) &&
  1142. DevicePen.CustomStartCap->IsValid() &&
  1143. ((customStartCapSize = DevicePen.CustomStartCap->GetDataSize()) > 0))
  1144. {
  1145. // startcap + sizeof custom cap + custom cap
  1146. dataSize += sizeof(INT32) + sizeof(INT32) + customStartCapSize;
  1147. }
  1148. }
  1149. else
  1150. {
  1151. dataSize += sizeof(INT32);
  1152. }
  1153. }
  1154. if (DevicePen.EndCap != LineCapFlat)
  1155. {
  1156. if (DevicePen.EndCap == LineCapCustom)
  1157. {
  1158. if ((DevicePen.CustomEndCap != NULL) &&
  1159. DevicePen.CustomEndCap->IsValid() &&
  1160. ((customEndCapSize = DevicePen.CustomEndCap->GetDataSize()) > 0))
  1161. {
  1162. // endcap + sizeof custom cap + custom cap
  1163. dataSize += sizeof(INT32) + sizeof(INT32) + customEndCapSize;
  1164. }
  1165. }
  1166. else
  1167. {
  1168. dataSize += sizeof(INT32);
  1169. }
  1170. }
  1171. if (DevicePen.Join != LineJoinMiter)
  1172. {
  1173. dataSize += sizeof(INT32);
  1174. }
  1175. if (DevicePen.MiterLimit != 10)
  1176. {
  1177. dataSize += sizeof(REAL);
  1178. }
  1179. // DashStyleCustom is handled by hasDashArray
  1180. if ((DevicePen.DashStyle != DashStyleSolid) && (DevicePen.DashStyle != DashStyleCustom))
  1181. {
  1182. dataSize += sizeof(INT32);
  1183. }
  1184. if (DevicePen.DashCap != LineCapFlat)
  1185. {
  1186. dataSize += sizeof(INT32);
  1187. }
  1188. if (DevicePen.DashOffset != 0)
  1189. {
  1190. dataSize += sizeof(REAL);
  1191. }
  1192. if ((DevicePen.DashStyle == DashStyleCustom) &&
  1193. (DevicePen.DashArray != NULL) &&
  1194. (DevicePen.DashCount > 0))
  1195. {
  1196. dataSize += sizeof(INT32) + (DevicePen.DashCount * sizeof(REAL));
  1197. }
  1198. if (DevicePen.PenAlignment != PenAlignmentCenter)
  1199. {
  1200. dataSize += sizeof(INT32);
  1201. }
  1202. if ((DevicePen.CompoundArray != NULL) && (DevicePen.CompoundCount > 0))
  1203. {
  1204. dataSize += sizeof(INT32) + (DevicePen.CompoundCount * sizeof(REAL));
  1205. }
  1206. dataSize += Brush->GetDataSize();
  1207. return dataSize;
  1208. }
  1209. /**************************************************************************\
  1210. *
  1211. * Function Description:
  1212. *
  1213. * Read the pen object from memory.
  1214. *
  1215. * Arguments:
  1216. *
  1217. * [IN] dataBuffer - the data that was read from the stream
  1218. * [IN] size - the size of the data
  1219. *
  1220. * Return Value:
  1221. *
  1222. * GpStatus - Ok or failure status
  1223. *
  1224. * Created:
  1225. *
  1226. * 4/26/1999 DCurtis
  1227. *
  1228. \**************************************************************************/
  1229. GpStatus
  1230. GpPen::SetData(
  1231. const BYTE * dataBuffer,
  1232. UINT size
  1233. )
  1234. {
  1235. if (dataBuffer == NULL)
  1236. {
  1237. WARNING(("dataBuffer is NULL"));
  1238. return InvalidParameter;
  1239. }
  1240. if (size < sizeof(PenData))
  1241. {
  1242. WARNING(("size too small"));
  1243. return InvalidParameter;
  1244. }
  1245. const PenData * penData = reinterpret_cast<const PenData *>(dataBuffer);
  1246. if (!penData->MajorVersionMatches())
  1247. {
  1248. WARNING(("Version number mismatch"));
  1249. return InvalidParameter;
  1250. }
  1251. InitDefaultState(penData->Width, static_cast<GpUnit>(penData->Unit));
  1252. dataBuffer += sizeof(PenData);
  1253. size -= sizeof(PenData);
  1254. if (penData->Flags & GDIP_PENFLAGS_TRANSFORM)
  1255. {
  1256. if (size < GDIP_MATRIX_SIZE)
  1257. {
  1258. WARNING(("size too small"));
  1259. goto ErrorExit;
  1260. }
  1261. DevicePen.Xform.SetMatrix((REAL *)dataBuffer);
  1262. dataBuffer += GDIP_MATRIX_SIZE;
  1263. size -= GDIP_MATRIX_SIZE;
  1264. }
  1265. if (penData->Flags & GDIP_PENFLAGS_STARTCAP)
  1266. {
  1267. if (size < sizeof(INT32))
  1268. {
  1269. WARNING(("size too small"));
  1270. goto ErrorExit;
  1271. }
  1272. DevicePen.StartCap = (GpLineCap) ((INT32 *)dataBuffer)[0];
  1273. dataBuffer += sizeof(INT32);
  1274. size -= sizeof(INT32);
  1275. }
  1276. if (penData->Flags & GDIP_PENFLAGS_ENDCAP)
  1277. {
  1278. if (size < sizeof(INT32))
  1279. {
  1280. WARNING(("size too small"));
  1281. goto ErrorExit;
  1282. }
  1283. DevicePen.EndCap = (GpLineCap) ((INT32 *)dataBuffer)[0];
  1284. dataBuffer += sizeof(INT32);
  1285. size -= sizeof(INT32);
  1286. }
  1287. if (penData->Flags & GDIP_PENFLAGS_JOIN)
  1288. {
  1289. if (size < sizeof(INT32))
  1290. {
  1291. WARNING(("size too small"));
  1292. goto ErrorExit;
  1293. }
  1294. DevicePen.Join = (GpLineJoin) ((INT32 *)dataBuffer)[0];
  1295. dataBuffer += sizeof(INT32);
  1296. size -= sizeof(INT32);
  1297. }
  1298. if (penData->Flags & GDIP_PENFLAGS_MITERLIMIT)
  1299. {
  1300. if (size < sizeof(REAL))
  1301. {
  1302. WARNING(("size too small"));
  1303. goto ErrorExit;
  1304. }
  1305. DevicePen.MiterLimit = ((REAL *)dataBuffer)[0];
  1306. dataBuffer += sizeof(REAL);
  1307. size -= sizeof(REAL);
  1308. }
  1309. if (penData->Flags & GDIP_PENFLAGS_DASHSTYLE)
  1310. {
  1311. if (size < sizeof(INT32))
  1312. {
  1313. WARNING(("size too small"));
  1314. goto ErrorExit;
  1315. }
  1316. this->SetDashStyle((GpDashStyle)((INT32 *)dataBuffer)[0]);
  1317. dataBuffer += sizeof(INT32);
  1318. size -= sizeof(INT32);
  1319. }
  1320. if (penData->Flags & GDIP_PENFLAGS_DASHCAP)
  1321. {
  1322. if (size < sizeof(INT32))
  1323. {
  1324. WARNING(("size too small"));
  1325. goto ErrorExit;
  1326. }
  1327. DevicePen.DashCap = (GpLineCap) ((INT32 *)dataBuffer)[0];
  1328. dataBuffer += sizeof(INT32);
  1329. size -= sizeof(INT32);
  1330. }
  1331. if (penData->Flags & GDIP_PENFLAGS_DASHOFFSET)
  1332. {
  1333. if (size < sizeof(REAL))
  1334. {
  1335. WARNING(("size too small"));
  1336. goto ErrorExit;
  1337. }
  1338. DevicePen.DashOffset = ((REAL *)dataBuffer)[0];
  1339. dataBuffer += sizeof(REAL);
  1340. size -= sizeof(REAL);
  1341. }
  1342. if (penData->Flags & GDIP_PENFLAGS_DASHARRAY)
  1343. {
  1344. if (size < sizeof(INT32))
  1345. {
  1346. WARNING(("size too small"));
  1347. goto ErrorExit;
  1348. }
  1349. INT count = ((INT32 *)dataBuffer)[0];
  1350. dataBuffer += sizeof(INT32);
  1351. size -= sizeof(INT32);
  1352. if (size < (count * sizeof(REAL)))
  1353. {
  1354. WARNING(("size too small"));
  1355. goto ErrorExit;
  1356. }
  1357. this->SetDashArray((REAL *)dataBuffer, count);
  1358. dataBuffer += (count * sizeof(REAL));
  1359. size -= (count * sizeof(REAL));
  1360. }
  1361. if (penData->Flags & GDIP_PENFLAGS_NONCENTER)
  1362. {
  1363. if (size < sizeof(INT32))
  1364. {
  1365. WARNING(("size too small"));
  1366. goto ErrorExit;
  1367. }
  1368. DevicePen.PenAlignment = (GpPenAlignment) ((INT32 *)dataBuffer)[0];
  1369. dataBuffer += sizeof(INT32);
  1370. size -= sizeof(INT32);
  1371. }
  1372. if (penData->Flags & GDIP_PENFLAGS_COMPOUNDARRAY)
  1373. {
  1374. if (size < sizeof(INT32))
  1375. {
  1376. WARNING(("size too small"));
  1377. goto ErrorExit;
  1378. }
  1379. INT count = ((INT32 *)dataBuffer)[0];
  1380. dataBuffer += sizeof(INT32);
  1381. size -= sizeof(INT32);
  1382. if (size < (count * sizeof(REAL)))
  1383. {
  1384. WARNING(("size too small"));
  1385. goto ErrorExit;
  1386. }
  1387. this->SetCompoundArray((REAL *)dataBuffer, count);
  1388. dataBuffer += (count * sizeof(REAL));
  1389. size -= (count * sizeof(REAL));
  1390. }
  1391. if (penData->Flags & GDIP_PENFLAGS_CUSTOMSTARTCAP)
  1392. {
  1393. if (size < sizeof(INT32))
  1394. {
  1395. WARNING(("size too small"));
  1396. goto ErrorExit;
  1397. }
  1398. UINT capSize = ((INT32 *)dataBuffer)[0];
  1399. dataBuffer += sizeof(INT32);
  1400. size -= sizeof(INT32);
  1401. if ((size < capSize) || (capSize < sizeof(ObjectTypeData)))
  1402. {
  1403. WARNING(("size too small"));
  1404. goto ErrorExit;
  1405. }
  1406. ASSERT(DevicePen.CustomStartCap == NULL);
  1407. DevicePen.CustomStartCap = (GpCustomLineCap *)GpObject::Factory(ObjectTypeCustomLineCap, (const ObjectData *)dataBuffer, capSize);
  1408. if ((DevicePen.CustomStartCap == NULL) ||
  1409. (DevicePen.CustomStartCap->SetData(dataBuffer, capSize) != Ok) ||
  1410. !DevicePen.CustomStartCap->IsValid())
  1411. {
  1412. WARNING(("Failure getting CustomStartCap"));
  1413. goto ErrorExit;
  1414. }
  1415. dataBuffer += capSize;
  1416. size -= capSize;
  1417. }
  1418. if (penData->Flags & GDIP_PENFLAGS_CUSTOMENDCAP)
  1419. {
  1420. if (size < sizeof(INT32))
  1421. {
  1422. WARNING(("size too small"));
  1423. goto ErrorExit;
  1424. }
  1425. UINT capSize = ((INT32 *)dataBuffer)[0];
  1426. dataBuffer += sizeof(INT32);
  1427. size -= sizeof(INT32);
  1428. if ((size < capSize) || (capSize < sizeof(ObjectTypeData)))
  1429. {
  1430. WARNING(("size too small"));
  1431. goto ErrorExit;
  1432. }
  1433. ASSERT(DevicePen.CustomEndCap == NULL);
  1434. DevicePen.CustomEndCap = (GpCustomLineCap *)GpObject::Factory(ObjectTypeCustomLineCap, (const ObjectData *)dataBuffer, capSize);
  1435. if ((DevicePen.CustomEndCap == NULL) ||
  1436. (DevicePen.CustomEndCap->SetData(dataBuffer, capSize) != Ok) ||
  1437. !DevicePen.CustomEndCap->IsValid())
  1438. {
  1439. WARNING(("Failure getting CustomEndCap"));
  1440. goto ErrorExit;
  1441. }
  1442. dataBuffer += capSize;
  1443. size -= capSize;
  1444. }
  1445. if (Brush != NULL)
  1446. {
  1447. Brush->Dispose();
  1448. Brush = NULL;
  1449. }
  1450. if (size >= sizeof(ObjectTypeData))
  1451. {
  1452. Brush = (GpBrush *)GpObject::Factory(ObjectTypeBrush, (const ObjectData *)dataBuffer, size);
  1453. if (Brush != NULL)
  1454. {
  1455. if ((Brush->SetData(dataBuffer, size) == Ok) && Brush->IsValid())
  1456. {
  1457. DevicePen.Brush = Brush->GetDeviceBrush();
  1458. SetValid(TRUE);
  1459. UpdateUid();
  1460. return Ok;
  1461. }
  1462. Brush->Dispose();
  1463. Brush = NULL;
  1464. }
  1465. }
  1466. WARNING(("Failure getting brush"));
  1467. ErrorExit:
  1468. SetValid(FALSE);
  1469. return GenericError;
  1470. }
  1471. GpStatus
  1472. GpPen::ColorAdjust(
  1473. GpRecolor * recolor,
  1474. ColorAdjustType type
  1475. )
  1476. {
  1477. ASSERT(recolor != NULL);
  1478. if (type == ColorAdjustTypeDefault)
  1479. {
  1480. type = ColorAdjustTypePen;
  1481. }
  1482. if (Brush != NULL)
  1483. {
  1484. Brush->ColorAdjust(recolor, type);
  1485. }
  1486. return Ok;
  1487. }
  1488. GpStatus
  1489. GpPen::GetColor(
  1490. ARGB *argb
  1491. ) const
  1492. {
  1493. if (Brush->GetBrushType() == BrushTypeSolidColor)
  1494. {
  1495. GpSolidFill * solidBrush = (GpSolidFill *) Brush;
  1496. *argb = solidBrush->GetColor().GetValue();
  1497. return Ok;
  1498. }
  1499. return InvalidParameter;
  1500. }
  1501. GpStatus
  1502. GpPen::SetColor(
  1503. GpColor * color
  1504. )
  1505. {
  1506. if (Brush->GetBrushType() == BrushTypeSolidColor)
  1507. {
  1508. GpSolidFill * solidBrush = (GpSolidFill *) Brush;
  1509. if (solidBrush->GetColor().GetValue() == color->GetValue())
  1510. {
  1511. return Ok;
  1512. }
  1513. // !!! bhouse why do we allocate another brush just to change the
  1514. // pen's color !!!!
  1515. }
  1516. GpSolidFill *newBrush = new GpSolidFill(*color);
  1517. if (newBrush != NULL)
  1518. {
  1519. if (newBrush->IsValid())
  1520. {
  1521. delete Brush;
  1522. Brush = newBrush;
  1523. DevicePen.Brush = Brush->GetDeviceBrush();
  1524. UpdateUid();
  1525. return Ok;
  1526. }
  1527. delete newBrush;
  1528. }
  1529. return GenericError;
  1530. }
  1531. GpStatus
  1532. GpPen::SetBrush(
  1533. GpBrush * brush
  1534. )
  1535. {
  1536. // Don't set the brush if it is the same color as the current one,
  1537. // because that makes metafiles unnecessarily large.
  1538. if ((Brush->GetBrushType() == BrushTypeSolidColor) &&
  1539. (brush->GetBrushType() == BrushTypeSolidColor))
  1540. {
  1541. GpSolidFill * solidBrush = (GpSolidFill *) Brush;
  1542. GpSolidFill * newSolidBrush = (GpSolidFill *) brush;
  1543. if(solidBrush->GetColor().GetValue() ==
  1544. newSolidBrush->GetColor().GetValue())
  1545. {
  1546. return Ok;
  1547. }
  1548. }
  1549. GpBrush * newBrush = brush->Clone();
  1550. if (newBrush != NULL)
  1551. {
  1552. if (newBrush->IsValid())
  1553. {
  1554. delete Brush;
  1555. Brush = newBrush;
  1556. DevicePen.Brush = Brush->GetDeviceBrush();
  1557. UpdateUid();
  1558. return Ok;
  1559. }
  1560. delete newBrush;
  1561. }
  1562. return GenericError;
  1563. }
  1564. GpPenType
  1565. GpPen::GetPenType(
  1566. )
  1567. {
  1568. GpPenType type = PenTypeUnknown;
  1569. if(Brush)
  1570. {
  1571. switch(Brush->GetBrushType())
  1572. {
  1573. case BrushTypeSolidColor:
  1574. type = PenTypeSolidColor;
  1575. break;
  1576. case BrushTypeHatchFill:
  1577. type = PenTypeHatchFill;
  1578. break;
  1579. case BrushTypeTextureFill:
  1580. type = PenTypeTextureFill;
  1581. break;
  1582. /*
  1583. case BrushRectGrad:
  1584. type = PenFillRectGrad;
  1585. break;
  1586. case BrushRadialGrad:
  1587. type = PenFillRadialGrad;
  1588. break;
  1589. case BrushTriangleGrad:
  1590. type = PenFillTriangleGrad;
  1591. break;
  1592. */
  1593. case BrushTypePathGradient:
  1594. type = PenTypePathGradient;
  1595. break;
  1596. case BrushTypeLinearGradient:
  1597. type = PenTypeLinearGradient;
  1598. break;
  1599. default:
  1600. break;
  1601. }
  1602. }
  1603. // We must implement LineTexture case.
  1604. return type;
  1605. }
  1606. /**************************************************************************\
  1607. *
  1608. * Function Description:
  1609. *
  1610. * Adjust the dash array for dash caps if present.
  1611. *
  1612. * Note that unlike line caps, dash caps do not extend the length
  1613. * of the subpath, they are inset. So we shorten the dash segments
  1614. * that draw a line and lengthen the dash segments that are spaces
  1615. * by a factor of 2x the dash unit in order to leave space for the
  1616. * caps that will be added by the widener.
  1617. *
  1618. * This fixes Whistler bug #126476.
  1619. *
  1620. * Arguments:
  1621. *
  1622. * [IN] dashCap - dash cap type
  1623. * [IN] dashUnit - dash size - typically the pen width
  1624. * [IN/OUT] dashArray - array containing the dash pattern that is adjusted.
  1625. * [IN] dashCount - count of elements in the dash array
  1626. *
  1627. * Return Value:
  1628. *
  1629. * None.
  1630. *
  1631. * History:
  1632. *
  1633. * 9/27/2000 jbronsk
  1634. * Created.
  1635. *
  1636. * 12/06/2000 aaronlie
  1637. * Moved from GpPath
  1638. *
  1639. \**************************************************************************/
  1640. VOID
  1641. GpPen::AdjustDashArrayForCaps(
  1642. REAL dashUnit,
  1643. REAL *dashArray,
  1644. INT dashCount
  1645. ) const
  1646. {
  1647. REAL adjustmentLength = 2.0f *
  1648. GetDashCapInsetLength(dashUnit);
  1649. if (adjustmentLength > 0.0f)
  1650. {
  1651. const REAL minimumDashValue = dashUnit * 0.001f; // a small number
  1652. for (int i = 0; i < dashCount; i++)
  1653. {
  1654. if (i & 0x1) // index is odd - so this is a space
  1655. {
  1656. // lengthen the spaces
  1657. dashArray[i] += adjustmentLength;
  1658. }
  1659. else // index is even - so this is a line
  1660. {
  1661. // shorten the lines
  1662. dashArray[i] -= adjustmentLength;
  1663. // check if we have made the dash too small
  1664. // (as in the case of 'dots')
  1665. if (dashArray[i] < minimumDashValue)
  1666. {
  1667. dashArray[i] = minimumDashValue;
  1668. }
  1669. }
  1670. }
  1671. }
  1672. }
  1673. /**************************************************************************\
  1674. *
  1675. * Function Description:
  1676. *
  1677. * Computes the length of the inset required to accomodate a particular
  1678. * dash cap type, since dash caps are contained within the dash length.
  1679. *
  1680. * Arguments:
  1681. *
  1682. * [IN] dashUnit - pen width
  1683. *
  1684. * Return Value:
  1685. *
  1686. * The amount that a dash needs to be inset on each end in order to
  1687. * accomodate any dash caps.
  1688. *
  1689. * History:
  1690. *
  1691. * 9/27/2000 jbronsk
  1692. * Created.
  1693. *
  1694. * 12/06/2000 aaronlie
  1695. * Moved from GpPath
  1696. *
  1697. \**************************************************************************/
  1698. REAL
  1699. GpPen::GetDashCapInsetLength(
  1700. REAL dashUnit
  1701. ) const
  1702. {
  1703. REAL insetLength = 0.0f;
  1704. // dash caps can only be flat, round, or triangle
  1705. switch(GetDashCap())
  1706. {
  1707. case LineCapFlat:
  1708. insetLength = 0.0f;
  1709. break;
  1710. case LineCapRound:
  1711. case LineCapTriangle:
  1712. insetLength = dashUnit * 0.5f;
  1713. break;
  1714. }
  1715. return insetLength;
  1716. }
  1717. /**************************************************************************\
  1718. *
  1719. * Function Description:
  1720. *
  1721. * Does a quick check to see if the path can be rendered as a solid
  1722. * pixel wide line.
  1723. *
  1724. * Arguments:
  1725. *
  1726. * [IN] cappedDpiX - the resolution of the x direction
  1727. * [IN] worldToDevice - World transform
  1728. *
  1729. * Return Value:
  1730. *
  1731. * TRUE if okay to be rendered as a one pixel line
  1732. *
  1733. * History:
  1734. *
  1735. * 12/17/1999 ikkof
  1736. * Created it.
  1737. *
  1738. \**************************************************************************/
  1739. BOOL
  1740. DpPen::IsOnePixelWideSolid(
  1741. const GpMatrix *worldToDevice,
  1742. REAL dpiX
  1743. ) const
  1744. {
  1745. return this->IsSimple() && this->IsOnePixelWide(worldToDevice, dpiX);
  1746. }
  1747. /**************************************************************************\
  1748. *
  1749. * Function Description:
  1750. *
  1751. * Does a quick check to see if the path can be rendered as a one
  1752. * pixel wide line.
  1753. *
  1754. * Arguments:
  1755. *
  1756. * [IN] cappedDpiX - the resolution of the x direction
  1757. * [IN] worldToDevice - World transform
  1758. *
  1759. * Return Value:
  1760. *
  1761. * TRUE if okay to be rendered as a one pixel line
  1762. *
  1763. * History:
  1764. *
  1765. * 10/6/2000 - peterost - factored out fron IsOnePixelWideSolid
  1766. *
  1767. \**************************************************************************/
  1768. BOOL
  1769. DpPen::IsOnePixelWide(
  1770. const GpMatrix *worldToDevice,
  1771. REAL dpiX
  1772. ) const
  1773. {
  1774. BOOL useOnePixelPath = FALSE;
  1775. const REAL minimumPenWidth = 1.5f;
  1776. // !!![andrewgo] This determination of a single pixel wide line is
  1777. // unbelievably expensive
  1778. // !!![andrewgo] This width check should be done simply using
  1779. // the world-to-device transform! It would be
  1780. // faster and simpler!
  1781. REAL width = this->Width;
  1782. GpUnit unit = this->Unit;
  1783. if(unit == UnitWorld)
  1784. {
  1785. if(worldToDevice == NULL || worldToDevice->IsTranslate())
  1786. {
  1787. if(width <= minimumPenWidth)
  1788. useOnePixelPath = TRUE;
  1789. }
  1790. else if(worldToDevice->IsTranslateScale())
  1791. {
  1792. REAL m11 = worldToDevice->GetM11();
  1793. REAL m22 = worldToDevice->GetM22();
  1794. REAL maxScale = max(REALABS(m11), REALABS(m22));
  1795. if(width*maxScale <= minimumPenWidth)
  1796. useOnePixelPath = TRUE;
  1797. }
  1798. else
  1799. {
  1800. // This is a general transform.
  1801. REAL majorR, minorR; // Radii for major and minor axis.
  1802. if(::GetMajorAndMinorAxis(
  1803. &majorR,
  1804. &minorR,
  1805. worldToDevice) == Ok)
  1806. {
  1807. if(width*majorR <= minimumPenWidth)
  1808. useOnePixelPath = TRUE;
  1809. }
  1810. }
  1811. }
  1812. else
  1813. {
  1814. // Since GDI+ only uses the World Uinit, this code is not called
  1815. // any more.
  1816. width = ::GetDeviceWidth(width, unit, dpiX);
  1817. if(width <= minimumPenWidth)
  1818. useOnePixelPath = TRUE;
  1819. }
  1820. return useOnePixelPath;
  1821. }