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.

2661 lines
64 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1999 Microsoft Corporation
  4. *
  5. * Module Name:
  6. *
  7. * Region.cpp
  8. *
  9. * Abstract:
  10. *
  11. * Implementation of GpRegion class
  12. *
  13. * Created:
  14. *
  15. * 2/3/1999 DCurtis
  16. *
  17. \**************************************************************************/
  18. #include "precomp.hpp"
  19. #define COMBINE_STEP_SIZE 4 // takes 2 for each combine operation
  20. LONG_PTR GpObject::Uniqueness = (0xdbc - 1); // for setting Uid of Objects
  21. /**************************************************************************\
  22. *
  23. * Function Description:
  24. *
  25. * Default constructor. Sets the default state of the region to
  26. * be infinite.
  27. *
  28. * Arguments:
  29. *
  30. * NONE
  31. *
  32. * Return Value:
  33. *
  34. * NONE
  35. *
  36. * Created:
  37. *
  38. * 2/3/1999 DCurtis
  39. *
  40. \**************************************************************************/
  41. GpRegion::GpRegion()
  42. {
  43. SetValid(TRUE); // default is valid
  44. // Default is infinite
  45. RegionOk = TRUE;
  46. Type = TypeInfinite;
  47. }
  48. /**************************************************************************\
  49. *
  50. * Function Description:
  51. *
  52. * Constructor. Sets the region to the specified rect.
  53. *
  54. * Arguments:
  55. *
  56. * [IN] rect - rect to initialize the region to
  57. *
  58. * Return Value:
  59. *
  60. * NONE
  61. *
  62. * Created:
  63. *
  64. * 2/3/1999 DCurtis
  65. *
  66. \**************************************************************************/
  67. GpRegion::GpRegion(
  68. const GpRectF * rect
  69. )
  70. {
  71. ASSERT(rect != NULL);
  72. SetValid(TRUE); // default is valid
  73. RegionOk = FALSE;
  74. X = rect->X;
  75. Y = rect->Y;
  76. Width = rect->Width;
  77. Height = rect->Height;
  78. Type = TypeRect;
  79. }
  80. /**************************************************************************\
  81. *
  82. * Function Description:
  83. *
  84. * Constructor. Sets the region to a copy of the specified path.
  85. *
  86. * Arguments:
  87. *
  88. * [IN] path - path to initialize the region to
  89. *
  90. * Return Value:
  91. *
  92. * NONE
  93. *
  94. * Created:
  95. *
  96. * 2/3/1999 DCurtis
  97. *
  98. \**************************************************************************/
  99. GpRegion::GpRegion(
  100. const GpPath * path
  101. )
  102. {
  103. ASSERT(path != NULL);
  104. SetValid(TRUE); // default is valid
  105. RegionOk = FALSE;
  106. Lazy = FALSE;
  107. Path = path->Clone();
  108. Type = (Path != NULL) ? TypePath : TypeNotValid;
  109. }
  110. /**************************************************************************\
  111. *
  112. * Function Description:
  113. *
  114. * Constructor. Sets the region using the specified region data buffer.
  115. *
  116. * Arguments:
  117. *
  118. * [IN] regionDataBuffer - should contain data that describes the region
  119. *
  120. * Return Value:
  121. *
  122. * NONE
  123. *
  124. * Created:
  125. *
  126. * 9/3/1999 DCurtis
  127. *
  128. \**************************************************************************/
  129. GpRegion::GpRegion(
  130. const BYTE * regionDataBuffer,
  131. UINT size
  132. )
  133. {
  134. ASSERT(regionDataBuffer != NULL);
  135. SetValid(TRUE); // default is valid
  136. RegionOk = FALSE;
  137. Type = TypeEmpty; // so FreePathData works correctly
  138. if (this->SetExternalData(regionDataBuffer, size) != Ok)
  139. {
  140. Type = TypeNotValid;
  141. }
  142. }
  143. /**************************************************************************\
  144. *
  145. * Function Description:
  146. *
  147. * Constructor. Sets the region to a copy of the specified path.
  148. *
  149. * Arguments:
  150. *
  151. * [IN] region - region to initialize the region to
  152. *
  153. * Return Value:
  154. *
  155. * NONE
  156. *
  157. * Created:
  158. *
  159. * 2/3/1999 DCurtis
  160. *
  161. \**************************************************************************/
  162. GpRegion::GpRegion(
  163. const GpRegion * region,
  164. BOOL lazy
  165. )
  166. {
  167. SetValid(TRUE); // default is valid
  168. RegionOk = FALSE;
  169. // We set the type here to avoid the assert in GpRegion::Set when the
  170. // uninitialized Type is equal to TypeNotValid
  171. Type = TypeEmpty;
  172. Set(region, lazy);
  173. }
  174. /**************************************************************************\
  175. *
  176. * Function Description:
  177. *
  178. * Destructor. Frees any copied path data associated with the region.
  179. *
  180. * Arguments:
  181. *
  182. * NONE
  183. *
  184. * Return Value:
  185. *
  186. * NONE
  187. *
  188. * Created:
  189. *
  190. * 2/3/1999 DCurtis
  191. *
  192. \**************************************************************************/
  193. GpRegion::~GpRegion()
  194. {
  195. FreePathData();
  196. }
  197. /**************************************************************************\
  198. *
  199. * Function Description:
  200. *
  201. * When a region is created from a path, a copy of that path is stored in
  202. * the region. This method frees up any of those copies that have been
  203. * saved in the region.
  204. *
  205. * It also resets the CombineData back to having no children.
  206. *
  207. * Arguments:
  208. *
  209. * NONE
  210. *
  211. * Return Value:
  212. *
  213. * NONE
  214. *
  215. * Created:
  216. *
  217. * 2/3/1999 DCurtis
  218. *
  219. \**************************************************************************/
  220. VOID
  221. GpRegion::FreePathData()
  222. {
  223. if (Type == TypePath)
  224. {
  225. if (!Lazy)
  226. {
  227. delete Path;
  228. }
  229. }
  230. else
  231. {
  232. INT count = CombineData.GetCount();
  233. if (count > 0)
  234. {
  235. RegionData * data = CombineData.GetDataBuffer();
  236. ASSERT (data != NULL);
  237. do
  238. {
  239. if ((data->Type == TypePath) && (!data->Lazy))
  240. {
  241. delete data->Path;
  242. }
  243. data++;
  244. } while (--count > 0);
  245. }
  246. CombineData.Reset();
  247. }
  248. }
  249. /**************************************************************************\
  250. *
  251. * Function Description:
  252. *
  253. * Set the region to the specified rectangle.
  254. *
  255. * Arguments:
  256. *
  257. * [IN] rect - the rect, in world units
  258. *
  259. * Return Value:
  260. *
  261. * GpStatus - Ok or failure status
  262. *
  263. * Created:
  264. *
  265. * 2/3/1999 DCurtis
  266. *
  267. \**************************************************************************/
  268. VOID
  269. GpRegion::Set(
  270. REAL x,
  271. REAL y,
  272. REAL width,
  273. REAL height
  274. )
  275. {
  276. ASSERT(IsValid());
  277. // handle flipped rects
  278. if (width < 0)
  279. {
  280. x += width;
  281. width = -width;
  282. }
  283. if (height < 0)
  284. {
  285. y += height;
  286. height = -height;
  287. }
  288. // crop to infinity
  289. if (x < INFINITE_MIN)
  290. {
  291. if (width < INFINITE_SIZE)
  292. {
  293. width -= (INFINITE_MIN - x);
  294. }
  295. x = INFINITE_MIN;
  296. }
  297. if (y < INFINITE_MIN)
  298. {
  299. if (height < INFINITE_SIZE)
  300. {
  301. height -= (INFINITE_MIN - y);
  302. }
  303. y = INFINITE_MIN;
  304. }
  305. if ((width > REAL_EPSILON) && (height > REAL_EPSILON))
  306. {
  307. if (width >= INFINITE_SIZE)
  308. {
  309. if (height >= INFINITE_SIZE)
  310. {
  311. SetInfinite();
  312. return;
  313. }
  314. width = INFINITE_SIZE; // crop to infinite
  315. }
  316. else if (height > INFINITE_SIZE)
  317. {
  318. height = INFINITE_SIZE; // crop to infinite
  319. }
  320. UpdateUid();
  321. if (RegionOk)
  322. {
  323. RegionOk = FALSE;
  324. DeviceRegion.SetEmpty();
  325. }
  326. FreePathData();
  327. X = x;
  328. Y = y;
  329. Width = width;
  330. Height = height;
  331. Type = TypeRect;
  332. return;
  333. }
  334. else
  335. {
  336. SetEmpty();
  337. }
  338. }
  339. /**************************************************************************\
  340. *
  341. * Function Description:
  342. *
  343. * Set the region to be infinite.
  344. *
  345. * Arguments:
  346. *
  347. * NONE
  348. *
  349. * Return Value:
  350. *
  351. * GpStatus - Ok or failure status
  352. *
  353. * Created:
  354. *
  355. * 2/9/1999 DCurtis
  356. *
  357. \**************************************************************************/
  358. VOID
  359. GpRegion::SetInfinite()
  360. {
  361. ASSERT(IsValid());
  362. UpdateUid();
  363. DeviceRegion.SetInfinite();
  364. RegionOk = TRUE;
  365. FreePathData();
  366. X = INFINITE_MIN;
  367. Y = INFINITE_MIN;
  368. Width = INFINITE_SIZE;
  369. Height = INFINITE_SIZE;
  370. Type = TypeInfinite;
  371. return;
  372. }
  373. /**************************************************************************\
  374. *
  375. * Function Description:
  376. *
  377. * Set the region to be empty.
  378. *
  379. * Arguments:
  380. *
  381. * NONE
  382. *
  383. * Return Value:
  384. *
  385. * GpStatus - Ok or failure status
  386. *
  387. * Created:
  388. *
  389. * 2/9/1999 DCurtis
  390. *
  391. \**************************************************************************/
  392. VOID
  393. GpRegion::SetEmpty()
  394. {
  395. ASSERT(IsValid());
  396. UpdateUid();
  397. DeviceRegion.SetEmpty();
  398. RegionOk = TRUE;
  399. FreePathData();
  400. X = 0;
  401. Y = 0;
  402. Width = 0;
  403. Height = 0;
  404. Type = TypeEmpty;
  405. return;
  406. }
  407. /**************************************************************************\
  408. *
  409. * Function Description:
  410. *
  411. * Set the region to the specified path.
  412. *
  413. * Arguments:
  414. *
  415. * [IN] path - the path, in world units
  416. *
  417. * Return Value:
  418. *
  419. * GpStatus - Ok or failure status
  420. *
  421. * Created:
  422. *
  423. * 2/3/1999 DCurtis
  424. *
  425. \**************************************************************************/
  426. GpStatus
  427. GpRegion::Set(
  428. const GpPath * path
  429. )
  430. {
  431. ASSERT(IsValid());
  432. ASSERT(path != NULL);
  433. UpdateUid();
  434. if (RegionOk)
  435. {
  436. RegionOk = FALSE;
  437. DeviceRegion.SetEmpty();
  438. }
  439. FreePathData();
  440. Lazy = FALSE;
  441. Path = path->Clone();
  442. if (Path != NULL)
  443. {
  444. Type = TypePath;
  445. return Ok;
  446. }
  447. Type = TypeNotValid;
  448. return GenericError;
  449. }
  450. /**************************************************************************\
  451. *
  452. * Function Description:
  453. *
  454. * Set the region to be a copy of the specified region.
  455. *
  456. * Arguments:
  457. *
  458. * [IN] region - the region to copy
  459. *
  460. * Return Value:
  461. *
  462. * GpStatus - Ok or failure status
  463. *
  464. * Created:
  465. *
  466. * 2/3/1999 DCurtis
  467. *
  468. \**************************************************************************/
  469. GpStatus
  470. GpRegion::Set(
  471. const GpRegion * region,
  472. BOOL lazy
  473. )
  474. {
  475. ASSERT(IsValid());
  476. ASSERT((region != NULL) && (region->IsValid()));
  477. if (region == this)
  478. {
  479. return Ok;
  480. }
  481. UpdateUid();
  482. if (RegionOk)
  483. {
  484. RegionOk = FALSE;
  485. DeviceRegion.SetEmpty();
  486. }
  487. FreePathData();
  488. if ((region->Type & REGIONTYPE_LEAF) != 0)
  489. {
  490. *this = *(const_cast<GpRegion *>(region));
  491. if (Type == TypePath)
  492. {
  493. if (!lazy)
  494. {
  495. Lazy = FALSE;
  496. if ((Path = Path->Clone()) == NULL)
  497. {
  498. Type = TypeNotValid;
  499. return GenericError;
  500. }
  501. }
  502. else // lazy copy
  503. {
  504. Lazy = TRUE;
  505. }
  506. }
  507. return Ok;
  508. }
  509. else
  510. {
  511. INT count = region->CombineData.GetCount();
  512. ASSERT(count > 0);
  513. Type = TypeNotValid;
  514. RegionData * data = CombineData.AddMultiple(count);
  515. if (data != NULL)
  516. {
  517. BOOL error = FALSE;
  518. GpMemcpy (data, region->CombineData.GetDataBuffer(),
  519. count * sizeof(*data));
  520. while (count--)
  521. {
  522. if (data->Type == TypePath)
  523. {
  524. if (!lazy)
  525. {
  526. data->Lazy = FALSE;
  527. if ((data->Path = data->Path->Clone()) == NULL)
  528. {
  529. data->Type = TypeNotValid;
  530. error = TRUE;
  531. // don't break out or else FreePathData will free
  532. // paths that don't belong to us.
  533. }
  534. }
  535. else // lazy copy
  536. {
  537. data->Lazy = TRUE;
  538. }
  539. }
  540. data++;
  541. }
  542. if (!error)
  543. {
  544. Type = region->Type;
  545. Left = region->Left;
  546. Right = region->Right;
  547. return Ok;
  548. }
  549. FreePathData();
  550. }
  551. }
  552. return GenericError;
  553. }
  554. /**************************************************************************\
  555. *
  556. * Function Description:
  557. *
  558. * Combine the region with the specified rect, using the boolean
  559. * operator specified by the type.
  560. *
  561. * Arguments:
  562. *
  563. * [IN] rect - the rect to combine with the current region
  564. * [IN] combineMode - the combine operator (and, or, xor, exclude, complement)
  565. *
  566. * Return Value:
  567. *
  568. * GpStatus - Ok or failure status
  569. *
  570. * Created:
  571. *
  572. * 2/3/1999 DCurtis
  573. *
  574. \**************************************************************************/
  575. GpStatus
  576. GpRegion::Combine(
  577. const GpRectF * rect,
  578. CombineMode combineMode
  579. )
  580. {
  581. ASSERT(IsValid());
  582. ASSERT(rect != NULL);
  583. ASSERT(CombineModeIsValid(combineMode));
  584. if (combineMode == CombineModeReplace)
  585. {
  586. this->Set(rect);
  587. return Ok;
  588. }
  589. if (Type == TypeInfinite)
  590. {
  591. if (combineMode == CombineModeIntersect)
  592. {
  593. this->Set(rect);
  594. return Ok;
  595. }
  596. else if (combineMode == CombineModeUnion)
  597. {
  598. return Ok; // nothing to do, already infinite
  599. }
  600. else if (combineMode == CombineModeComplement)
  601. {
  602. this->SetEmpty();
  603. return Ok;
  604. }
  605. }
  606. else if (Type == TypeEmpty)
  607. {
  608. if ((combineMode == CombineModeUnion) ||
  609. (combineMode == CombineModeXor) ||
  610. (combineMode == CombineModeComplement))
  611. {
  612. this->Set(rect);
  613. }
  614. // if combineMode is Intersect or Exclude, just leave it empty
  615. return Ok;
  616. }
  617. // Now we know this region is not empty
  618. REAL x = rect->X;
  619. REAL y = rect->Y;
  620. REAL width = rect->Width;
  621. REAL height = rect->Height;
  622. // handle flipped rects
  623. if (width < 0)
  624. {
  625. x += width;
  626. width = -width;
  627. }
  628. if (height < 0)
  629. {
  630. y += height;
  631. height = -height;
  632. }
  633. // crop to infinity
  634. if (x < INFINITE_MIN)
  635. {
  636. if (width < INFINITE_SIZE)
  637. {
  638. width -= (INFINITE_MIN - x);
  639. }
  640. x = INFINITE_MIN;
  641. }
  642. if (y < INFINITE_MIN)
  643. {
  644. if (height < INFINITE_SIZE)
  645. {
  646. height -= (INFINITE_MIN - y);
  647. }
  648. y = INFINITE_MIN;
  649. }
  650. BOOL isEmptyRect = ((width <= REAL_EPSILON) || (height <= REAL_EPSILON));
  651. if (isEmptyRect)
  652. {
  653. if ((combineMode == CombineModeIntersect) ||
  654. (combineMode == CombineModeComplement))
  655. {
  656. SetEmpty();
  657. }
  658. // if combineMode is Union or Xor or Exclude, just leave it alone
  659. return Ok;
  660. }
  661. // Now we know the rect is not empty
  662. // See if the rect is infinite
  663. if (width >= INFINITE_SIZE)
  664. {
  665. if (height >= INFINITE_SIZE)
  666. {
  667. GpRegion infiniteRegion;
  668. return this->Combine(&infiniteRegion, combineMode);
  669. }
  670. width = INFINITE_SIZE; // crop to infinite
  671. }
  672. else if (height > INFINITE_SIZE)
  673. {
  674. height = INFINITE_SIZE; // crop to infinite
  675. }
  676. // The rect is neither infinite nor empty
  677. UpdateUid();
  678. if (RegionOk)
  679. {
  680. RegionOk = FALSE;
  681. DeviceRegion.SetEmpty();
  682. }
  683. INT index = CombineData.GetCount();
  684. RegionData * data = CombineData.AddMultiple(2);
  685. if (data != NULL)
  686. {
  687. data[0] = *this;
  688. data[1].Type = TypeRect;
  689. data[1].X = x;
  690. data[1].Y = y;
  691. data[1].Width = width;
  692. data[1].Height = height;
  693. Type = (NodeType)combineMode;
  694. Left = index;
  695. Right = index + 1;
  696. return Ok;
  697. }
  698. FreePathData();
  699. Type = TypeNotValid;
  700. return GenericError;
  701. }
  702. /**************************************************************************\
  703. *
  704. * Function Description:
  705. *
  706. * Combine the region with the specified path, using the boolean
  707. * operator specified by the type.
  708. *
  709. * Arguments:
  710. *
  711. * [IN] path - the path to combine with the current region
  712. * [IN] combineMode - the combine operator (and, or, xor, exclude, complement)
  713. *
  714. * Return Value:
  715. *
  716. * GpStatus - Ok or failure status
  717. *
  718. * Created:
  719. *
  720. * 2/3/1999 DCurtis
  721. *
  722. \**************************************************************************/
  723. GpStatus
  724. GpRegion::Combine(
  725. const GpPath * path,
  726. CombineMode combineMode
  727. )
  728. {
  729. ASSERT(IsValid());
  730. ASSERT(path != NULL);
  731. ASSERT(CombineModeIsValid(combineMode));
  732. if (combineMode == CombineModeReplace)
  733. {
  734. return this->Set(path);
  735. }
  736. if (Type == TypeInfinite)
  737. {
  738. if (combineMode == CombineModeIntersect)
  739. {
  740. this->Set(path);
  741. return Ok;
  742. }
  743. else if (combineMode == CombineModeUnion)
  744. {
  745. return Ok; // nothing to do, already infinite
  746. }
  747. else if (combineMode == CombineModeComplement)
  748. {
  749. this->SetEmpty();
  750. return Ok;
  751. }
  752. }
  753. else if (Type == TypeEmpty)
  754. {
  755. if ((combineMode == CombineModeUnion) ||
  756. (combineMode == CombineModeXor) ||
  757. (combineMode == CombineModeComplement))
  758. {
  759. this->Set(path);
  760. }
  761. // if combineMode is Intersect or Exclude, just leave it empty
  762. return Ok;
  763. }
  764. // Now we know this region is not empty
  765. if (RegionOk)
  766. {
  767. RegionOk = FALSE;
  768. DeviceRegion.SetEmpty();
  769. }
  770. GpPath * pathCopy = path->Clone();
  771. if (pathCopy != NULL)
  772. {
  773. INT index = CombineData.GetCount();
  774. RegionData * data = CombineData.AddMultiple(2);
  775. if (data != NULL)
  776. {
  777. data[0] = *this;
  778. data[1].Type = TypePath;
  779. data[1].Lazy = FALSE;
  780. data[1].Path = pathCopy;
  781. Type = (NodeType)combineMode;
  782. Left = index;
  783. Right = index + 1;
  784. UpdateUid();
  785. return Ok;
  786. }
  787. delete pathCopy;
  788. }
  789. FreePathData();
  790. Type = TypeNotValid;
  791. return GenericError;
  792. }
  793. /**************************************************************************\
  794. *
  795. * Function Description:
  796. *
  797. * Combine the region with the specified region, using the boolean
  798. * operator specified by the type.
  799. *
  800. * Arguments:
  801. *
  802. * [IN] region - the region to combine with the current region
  803. * [IN] combineMode - the combine operator (and, or, xor, exclude, complement)
  804. *
  805. * Return Value:
  806. *
  807. * GpStatus - Ok or failure status
  808. *
  809. * Created:
  810. *
  811. * 2/3/1999 DCurtis
  812. *
  813. \**************************************************************************/
  814. GpStatus
  815. GpRegion::Combine(
  816. GpRegion * region,
  817. CombineMode combineMode
  818. )
  819. {
  820. ASSERT(IsValid());
  821. ASSERT((region != NULL) && region->IsValid());
  822. ASSERT(CombineModeIsValid(combineMode));
  823. if (combineMode == CombineModeReplace)
  824. {
  825. return this->Set(region);
  826. }
  827. if (region->Type == TypeEmpty)
  828. {
  829. if ((combineMode == CombineModeIntersect) ||
  830. (combineMode == CombineModeComplement))
  831. {
  832. SetEmpty();
  833. }
  834. // if combineMode is Union or Xor or Exclude, just leave it alone
  835. return Ok;
  836. }
  837. // Now we know the input region is not empty
  838. if (region->Type == TypeInfinite)
  839. {
  840. if (combineMode == CombineModeIntersect)
  841. {
  842. return Ok;
  843. }
  844. else if (combineMode == CombineModeUnion)
  845. {
  846. SetInfinite();
  847. return Ok;
  848. }
  849. else if ((combineMode == CombineModeXor) ||
  850. (combineMode == CombineModeComplement))
  851. {
  852. if (Type == TypeInfinite)
  853. {
  854. SetEmpty();
  855. return Ok;
  856. }
  857. }
  858. if (combineMode == CombineModeExclude)
  859. {
  860. SetEmpty();
  861. return Ok;
  862. }
  863. }
  864. if (Type == TypeInfinite)
  865. {
  866. if (combineMode == CombineModeIntersect)
  867. {
  868. this->Set(region);
  869. return Ok;
  870. }
  871. else if (combineMode == CombineModeUnion)
  872. {
  873. return Ok; // nothing to do, already infinite
  874. }
  875. else if (combineMode == CombineModeComplement)
  876. {
  877. this->SetEmpty();
  878. return Ok;
  879. }
  880. }
  881. else if (Type == TypeEmpty)
  882. {
  883. if ((combineMode == CombineModeUnion) ||
  884. (combineMode == CombineModeXor) ||
  885. (combineMode == CombineModeComplement))
  886. {
  887. this->Set(region);
  888. }
  889. // if combineMode is Intersect or Exclude, just leave it empty
  890. return Ok;
  891. }
  892. // Now we know this region is not empty
  893. if (RegionOk)
  894. {
  895. RegionOk = FALSE;
  896. DeviceRegion.SetEmpty();
  897. }
  898. INT regionCount = region->CombineData.GetCount();
  899. INT index = CombineData.GetCount();
  900. RegionData * data = CombineData.AddMultiple(2 + regionCount);
  901. if (data != NULL)
  902. {
  903. data[regionCount] = *this;
  904. data[regionCount + 1] = *region;
  905. if (regionCount > 0)
  906. {
  907. RegionData * srcData = region->CombineData.GetDataBuffer();
  908. INT i = 0;
  909. BOOL error = FALSE;
  910. GpPath * path;
  911. do
  912. {
  913. data[i] = srcData[i];
  914. if ((data[i].Type & REGIONTYPE_LEAF) == 0)
  915. {
  916. data[i].Left += index;
  917. data[i].Right += index;
  918. }
  919. else if (data[i].Type == TypePath)
  920. {
  921. data[i].Lazy = FALSE;
  922. path = data[i].Path->Clone();
  923. data[i].Path = path;
  924. if (path == NULL)
  925. {
  926. data[i].Type = TypeNotValid;
  927. error = TRUE;
  928. // don't break out
  929. }
  930. }
  931. } while (++i < regionCount);
  932. data[regionCount+1].Left += index;
  933. data[regionCount+1].Right += index;
  934. index += regionCount;
  935. if (error)
  936. {
  937. goto ErrorExit;
  938. }
  939. }
  940. else if (region->Type == TypePath)
  941. {
  942. data[1].Lazy = FALSE;
  943. data[1].Path = region->Path->Clone();
  944. if (data[1].Path == NULL)
  945. {
  946. data[1].Type = TypeNotValid;
  947. goto ErrorExit;
  948. }
  949. }
  950. Type = (NodeType)combineMode;
  951. Left = index;
  952. Right = index + 1;
  953. UpdateUid();
  954. return Ok;
  955. }
  956. ErrorExit:
  957. FreePathData();
  958. Type = TypeNotValid;
  959. return GenericError;
  960. }
  961. GpStatus
  962. GpRegion::CreateLeafDeviceRegion(
  963. const RegionData * regionData,
  964. DpRegion * region
  965. ) const
  966. {
  967. GpStatus status = GenericError;
  968. switch (regionData->Type)
  969. {
  970. case TypeRect:
  971. if ((regionData->Width > 0) &&
  972. (regionData->Height > 0))
  973. {
  974. // If the transform is a simple scaling transform, life is a
  975. // little easier:
  976. if (Matrix.IsTranslateScale())
  977. {
  978. GpRectF rect(regionData->X,
  979. regionData->Y,
  980. regionData->Width,
  981. regionData->Height);
  982. Matrix.TransformRect(rect);
  983. // Use ceiling to stay compatible with rasterizer
  984. // Don't take the ceiling of the width directly,
  985. // because it introduces additional round-off error.
  986. // For example, if rect.X is 1.7 and rect.Width is 47.2,
  987. // then if we took the ceiling of the width, the right
  988. // coordinate will end up being 50, instead of 49.
  989. INT xMin = RasterizerCeiling(rect.X);
  990. INT yMin = RasterizerCeiling(rect.Y);
  991. INT xMax = RasterizerCeiling(rect.GetRight());
  992. INT yMax = RasterizerCeiling(rect.GetBottom());
  993. region->Set(xMin, yMin, xMax - xMin, yMax - yMin);
  994. status = Ok;
  995. }
  996. else
  997. {
  998. GpPointF points[4];
  999. REAL left;
  1000. REAL right;
  1001. REAL top;
  1002. REAL bottom;
  1003. left = regionData->X;
  1004. top = regionData->Y;
  1005. right = regionData->X + regionData->Width;
  1006. bottom = regionData->Y + regionData->Height;
  1007. points[0].X = left;
  1008. points[0].Y = top;
  1009. points[1].X = right;
  1010. points[1].Y = top;
  1011. points[2].X = right;
  1012. points[2].Y = bottom;
  1013. points[3].X = left;
  1014. points[3].Y = bottom;
  1015. const INT stackCount = 4;
  1016. GpPointF stackPoints[stackCount];
  1017. BYTE stackTypes[stackCount];
  1018. GpPath path(points,
  1019. 4,
  1020. stackPoints,
  1021. stackTypes,
  1022. stackCount,
  1023. FillModeAlternate,
  1024. DpPath::Convex);
  1025. if (path.IsValid())
  1026. {
  1027. status = region->Set(&path, &Matrix);
  1028. }
  1029. }
  1030. }
  1031. else
  1032. {
  1033. region->SetEmpty();
  1034. status = Ok;
  1035. }
  1036. break;
  1037. case TypePath:
  1038. status = region->Set(regionData->Path, &Matrix);
  1039. break;
  1040. case TypeEmpty:
  1041. region->SetEmpty();
  1042. status = Ok;
  1043. break;
  1044. case TypeInfinite:
  1045. region->SetInfinite();
  1046. status = Ok;
  1047. break;
  1048. default:
  1049. ASSERT(0);
  1050. break;
  1051. }
  1052. return status;
  1053. }
  1054. /**************************************************************************\
  1055. *
  1056. * Function Description:
  1057. *
  1058. * Creates a DpRegion (device coordinate region) using the data in the
  1059. * specified RegionData node and using the current transformation matrix.
  1060. * This may involve creating a region for children nodes and then combining
  1061. * the children into a single device region.
  1062. *
  1063. * Arguments:
  1064. *
  1065. * [IN] regionData - the world coordinate region to convert to device region
  1066. * [OUT] region - the created/combined device region
  1067. *
  1068. * Return Value:
  1069. *
  1070. * GpStatus - Ok or failure status
  1071. *
  1072. * Created:
  1073. *
  1074. * 2/3/1999 DCurtis
  1075. *
  1076. \**************************************************************************/
  1077. GpStatus
  1078. GpRegion::CreateDeviceRegion(
  1079. const RegionData * regionData,
  1080. DpRegion * region
  1081. ) const
  1082. {
  1083. ASSERT(IsValid());
  1084. GpStatus status;
  1085. RegionData * regionDataLeft;
  1086. regionDataLeft = &(CombineData[regionData->Left]);
  1087. if ((regionDataLeft->Type & REGIONTYPE_LEAF) != 0)
  1088. {
  1089. status = CreateLeafDeviceRegion(regionDataLeft, region);
  1090. }
  1091. else
  1092. {
  1093. status = CreateDeviceRegion(regionDataLeft, region);
  1094. }
  1095. if (status == Ok)
  1096. {
  1097. DpRegion regionRight;
  1098. RegionData * regionDataRight;
  1099. regionDataRight = &(CombineData[regionData->Right]);
  1100. if ((regionDataRight->Type & REGIONTYPE_LEAF) != 0)
  1101. {
  1102. status = CreateLeafDeviceRegion(regionDataRight, &regionRight);
  1103. }
  1104. else
  1105. {
  1106. status = CreateDeviceRegion(regionDataRight, &regionRight);
  1107. }
  1108. if (status == Ok)
  1109. {
  1110. switch (regionData->Type)
  1111. {
  1112. case TypeAnd:
  1113. status = region->And(&regionRight);
  1114. break;
  1115. case TypeOr:
  1116. status = region->Or(&regionRight);
  1117. break;
  1118. case TypeXor:
  1119. status = region->Xor(&regionRight);
  1120. break;
  1121. case TypeExclude:
  1122. status = region->Exclude(&regionRight);
  1123. break;
  1124. case TypeComplement:
  1125. status = region->Complement(&regionRight);
  1126. break;
  1127. default:
  1128. ASSERT(0);
  1129. break;
  1130. }
  1131. }
  1132. }
  1133. return status;
  1134. }
  1135. /**************************************************************************\
  1136. *
  1137. * Function Description:
  1138. *
  1139. * Checks if the current DeviceRegion is up-to-date with the specified
  1140. * matrix. If not, then it recreates the DeviceRegion using the matrix.
  1141. *
  1142. * Arguments:
  1143. *
  1144. * [IN] matrix - the world-to-device transformation matrix
  1145. *
  1146. * Return Value:
  1147. *
  1148. * GpStatus - Ok or failure status
  1149. *
  1150. * Created:
  1151. *
  1152. * 2/3/1999 DCurtis
  1153. *
  1154. \**************************************************************************/
  1155. GpStatus
  1156. GpRegion::UpdateDeviceRegion(
  1157. GpMatrix * matrix
  1158. ) const
  1159. {
  1160. ASSERT(IsValid());
  1161. if (RegionOk && matrix->IsEqual(&Matrix))
  1162. {
  1163. return Ok;
  1164. }
  1165. Matrix = *matrix;
  1166. GpStatus status;
  1167. if ((this->Type & REGIONTYPE_LEAF) != 0)
  1168. {
  1169. status = CreateLeafDeviceRegion(this, &DeviceRegion);
  1170. }
  1171. else
  1172. {
  1173. status = CreateDeviceRegion(this, &DeviceRegion);
  1174. }
  1175. RegionOk = (status == Ok);
  1176. return status;
  1177. }
  1178. /**************************************************************************\
  1179. *
  1180. * Function Description:
  1181. *
  1182. * Get the bounds of the region, in world units.
  1183. *
  1184. * Arguments:
  1185. *
  1186. * [IN] matrix - world-to-device transformation matrix
  1187. * [OUT] bounds - bounding rect of region, in world units
  1188. *
  1189. * Return Value:
  1190. *
  1191. * GpStatus - Ok or failure status
  1192. *
  1193. * Created:
  1194. *
  1195. * 2/3/1999 DCurtis
  1196. *
  1197. \**************************************************************************/
  1198. GpStatus
  1199. GpRegion::GetBounds(
  1200. GpGraphics * graphics,
  1201. GpRectF * bounds,
  1202. BOOL device
  1203. ) const
  1204. {
  1205. ASSERT((graphics != NULL) && (bounds != NULL));
  1206. ASSERT(IsValid() && graphics->IsValid());
  1207. // Note we can't lock graphics, cause it gets locked in its calls
  1208. GpStatus status = Ok;
  1209. switch (Type)
  1210. {
  1211. case TypeRect:
  1212. if (!device)
  1213. {
  1214. bounds->X = X;
  1215. bounds->Y = Y;
  1216. bounds->Width = Width;
  1217. bounds->Height = Height;
  1218. }
  1219. else
  1220. {
  1221. GpMatrix worldToDevice;
  1222. graphics->GetWorldToDeviceTransform(&worldToDevice);
  1223. TransformBounds(&worldToDevice, X, Y, X + Width, Y + Height,bounds);
  1224. }
  1225. break;
  1226. case TypePath:
  1227. {
  1228. GpMatrix worldToDevice;
  1229. if (device)
  1230. {
  1231. graphics->GetWorldToDeviceTransform(&worldToDevice);
  1232. }
  1233. // else leave it as identity
  1234. Path->GetBounds(bounds, &worldToDevice);
  1235. }
  1236. break;
  1237. case TypeInfinite:
  1238. bounds->X = INFINITE_MIN;
  1239. bounds->Y = INFINITE_MIN;
  1240. bounds->Width = INFINITE_SIZE;
  1241. bounds->Height = INFINITE_SIZE;
  1242. break;
  1243. case TypeAnd:
  1244. case TypeOr:
  1245. case TypeXor:
  1246. case TypeExclude:
  1247. case TypeComplement:
  1248. {
  1249. GpMatrix worldToDevice;
  1250. graphics->GetWorldToDeviceTransform(&worldToDevice);
  1251. if (UpdateDeviceRegion(&worldToDevice) == Ok)
  1252. {
  1253. GpRect deviceBounds;
  1254. DeviceRegion.GetBounds(&deviceBounds);
  1255. if (device)
  1256. {
  1257. bounds->X = TOREAL(deviceBounds.X);
  1258. bounds->Y = TOREAL(deviceBounds.Y);
  1259. bounds->Width = TOREAL(deviceBounds.Width);
  1260. bounds->Height = TOREAL(deviceBounds.Height);
  1261. break;
  1262. }
  1263. else
  1264. {
  1265. GpMatrix deviceToWorld;
  1266. if (graphics->GetDeviceToWorldTransform(&deviceToWorld)==Ok)
  1267. {
  1268. TransformBounds(
  1269. &deviceToWorld,
  1270. TOREAL(deviceBounds.X),
  1271. TOREAL(deviceBounds.Y),
  1272. TOREAL(deviceBounds.X + deviceBounds.Width),
  1273. TOREAL(deviceBounds.Y + deviceBounds.Height),
  1274. bounds);
  1275. break;
  1276. }
  1277. }
  1278. }
  1279. }
  1280. status = GenericError;
  1281. // FALLTHRU
  1282. default: // TypeEmpty
  1283. bounds->X = 0;
  1284. bounds->Y = 0;
  1285. bounds->Width = 0;
  1286. bounds->Height = 0;
  1287. break;
  1288. }
  1289. return status;
  1290. }
  1291. /**************************************************************************\
  1292. *
  1293. * Function Description:
  1294. *
  1295. * Get the bounds of the region, in device units.
  1296. *
  1297. * Arguments:
  1298. *
  1299. * [IN] matrix - world-to-device transformation matrix
  1300. * [OUT] bounds - bounding rect of region, in device units
  1301. *
  1302. * Return Value:
  1303. *
  1304. * GpStatus - Ok or failure status
  1305. *
  1306. * Created:
  1307. *
  1308. * 2/3/1999 DCurtis
  1309. *
  1310. \**************************************************************************/
  1311. GpStatus
  1312. GpRegion::GetBounds(
  1313. GpMatrix * matrix,
  1314. GpRect * bounds
  1315. ) const
  1316. {
  1317. ASSERT(IsValid());
  1318. ASSERT((matrix != NULL) && (bounds != NULL));
  1319. GpStatus status = Ok;
  1320. switch (Type)
  1321. {
  1322. case TypeInfinite:
  1323. bounds->X = INFINITE_MIN;
  1324. bounds->Y = INFINITE_MIN;
  1325. bounds->Width = INFINITE_SIZE;
  1326. bounds->Height = INFINITE_SIZE;
  1327. break;
  1328. default:
  1329. if (UpdateDeviceRegion(matrix) == Ok)
  1330. {
  1331. DeviceRegion.GetBounds(bounds);
  1332. break;
  1333. }
  1334. status = GenericError;
  1335. // FALLTHRU
  1336. case TypeEmpty:
  1337. bounds->X = 0;
  1338. bounds->Y = 0;
  1339. bounds->Width = 0;
  1340. bounds->Height = 0;
  1341. break;
  1342. }
  1343. return status;
  1344. }
  1345. /**************************************************************************\
  1346. *
  1347. * Function Description:
  1348. *
  1349. * Get the HRGN corresponding to the region
  1350. *
  1351. * Arguments:
  1352. *
  1353. * [IN] graphics - a reference graphics for conversion to device units
  1354. * (can be NULL)
  1355. * [OUT] hRgn - the GDI region
  1356. *
  1357. * Return Value:
  1358. *
  1359. * GpStatus - Ok or failure status
  1360. *
  1361. * Created:
  1362. *
  1363. * 7/6/1999 DCurtis
  1364. *
  1365. \**************************************************************************/
  1366. GpStatus
  1367. GpRegion::GetHRgn(
  1368. GpGraphics * graphics,
  1369. HRGN * hRgn
  1370. ) const
  1371. {
  1372. ASSERT(IsValid());
  1373. ASSERT(hRgn != NULL);
  1374. GpMatrix worldToDevice;
  1375. if (graphics != NULL)
  1376. {
  1377. graphics->GetWorldToDeviceTransform(&worldToDevice);
  1378. }
  1379. if (UpdateDeviceRegion(&worldToDevice) == Ok)
  1380. {
  1381. if ((*hRgn = DeviceRegion.GetHRgn()) != (HRGN)INVALID_HANDLE_VALUE)
  1382. {
  1383. return Ok;
  1384. }
  1385. }
  1386. else
  1387. {
  1388. *hRgn = (HRGN)INVALID_HANDLE_VALUE;
  1389. }
  1390. return GenericError;
  1391. }
  1392. GpStatus
  1393. GpRegion::GetRegionScans(
  1394. GpRect * rects,
  1395. INT * count,
  1396. const GpMatrix * matrix
  1397. ) const
  1398. {
  1399. ASSERT(IsValid());
  1400. ASSERT(count != NULL);
  1401. ASSERT(matrix != NULL);
  1402. if (UpdateDeviceRegion(const_cast<GpMatrix*>(matrix)) == Ok)
  1403. {
  1404. *count = DeviceRegion.GetRects(rects);
  1405. return Ok;
  1406. }
  1407. else
  1408. {
  1409. *count = 0;
  1410. }
  1411. return GenericError;
  1412. }
  1413. GpStatus
  1414. GpRegion::GetRegionScans(
  1415. GpRectF * rects,
  1416. INT * count,
  1417. const GpMatrix * matrix
  1418. ) const
  1419. {
  1420. ASSERT(IsValid());
  1421. ASSERT(count != NULL);
  1422. ASSERT(matrix != NULL);
  1423. if (UpdateDeviceRegion(const_cast<GpMatrix*>(matrix)) == Ok)
  1424. {
  1425. *count = DeviceRegion.GetRects(rects);
  1426. return Ok;
  1427. }
  1428. else
  1429. {
  1430. *count = 0;
  1431. }
  1432. return GenericError;
  1433. }
  1434. /**************************************************************************\
  1435. *
  1436. * Function Description:
  1437. *
  1438. * Determine if the specified point is visible (inside) the region.
  1439. *
  1440. * Arguments:
  1441. *
  1442. * [IN] point - the point, in world units
  1443. * [IN] matrix - the world-to-device transformation matrix to use
  1444. * [OUT] isVisible - if the point is visible or not
  1445. *
  1446. * Return Value:
  1447. *
  1448. * GpStatus - Ok or failure status
  1449. *
  1450. * Created:
  1451. *
  1452. * 2/3/1999 DCurtis
  1453. *
  1454. \**************************************************************************/
  1455. GpStatus
  1456. GpRegion::IsVisible (
  1457. GpPointF * point,
  1458. GpMatrix * matrix,
  1459. BOOL * isVisible
  1460. ) const
  1461. {
  1462. ASSERT(IsValid());
  1463. ASSERT(matrix != NULL);
  1464. ASSERT(point != NULL);
  1465. ASSERT(isVisible != NULL);
  1466. if (UpdateDeviceRegion(matrix) == Ok)
  1467. {
  1468. GpPointF transformedPoint = *point;
  1469. matrix->Transform(&transformedPoint);
  1470. *isVisible = DeviceRegion.PointInside(GpRound(transformedPoint.X),
  1471. GpRound(transformedPoint.Y));
  1472. return Ok;
  1473. }
  1474. *isVisible = FALSE;
  1475. return GenericError;
  1476. }
  1477. /**************************************************************************\
  1478. *
  1479. * Function Description:
  1480. *
  1481. * Determine if the specified rect is inside or overlaps the region.
  1482. *
  1483. * Arguments:
  1484. *
  1485. * [IN] rect - the rect, in world units
  1486. * [IN] matrix - the world-to-device transformation matrix to use
  1487. * [OUT] isVisible - if the rect is visible or not
  1488. *
  1489. * Return Value:
  1490. *
  1491. * GpStatus - Ok or failure status
  1492. *
  1493. * Created:
  1494. *
  1495. * 2/3/1999 DCurtis
  1496. *
  1497. \**************************************************************************/
  1498. GpStatus
  1499. GpRegion::IsVisible(
  1500. GpRectF * rect,
  1501. GpMatrix * matrix,
  1502. BOOL * isVisible
  1503. ) const
  1504. {
  1505. ASSERT(IsValid());
  1506. ASSERT(matrix != NULL);
  1507. ASSERT(rect != NULL);
  1508. if (UpdateDeviceRegion(matrix) == Ok)
  1509. {
  1510. // If the transform is a simple scaling transform, life is a
  1511. // little easier:
  1512. if (Matrix.IsTranslateScale())
  1513. {
  1514. GpRectF transformRect(*rect);
  1515. Matrix.TransformRect(transformRect);
  1516. // Use ceiling to stay compatible with rasterizer
  1517. INT x = GpCeiling(transformRect.X);
  1518. INT y = GpCeiling(transformRect.Y);
  1519. *isVisible = DeviceRegion.RectVisible(
  1520. x, y,
  1521. x + GpCeiling(transformRect.Width),
  1522. y + GpCeiling(transformRect.Height));
  1523. return Ok;
  1524. }
  1525. else
  1526. {
  1527. REAL left = rect->X;
  1528. REAL top = rect->Y;
  1529. REAL right = rect->X + rect->Width;
  1530. REAL bottom = rect->Y + rect->Height;
  1531. GpRectF bounds;
  1532. GpRect deviceBounds;
  1533. GpRect regionBounds;
  1534. TransformBounds(matrix, left, top, right, bottom, &bounds);
  1535. GpStatus status = BoundsFToRect(&bounds, &deviceBounds);
  1536. DeviceRegion.GetBounds(&regionBounds);
  1537. // try trivial reject
  1538. if (status != Ok || !regionBounds.IntersectsWith(deviceBounds))
  1539. {
  1540. *isVisible = FALSE;
  1541. return status;
  1542. }
  1543. // couldn't reject, so do full test
  1544. GpPointF points[4];
  1545. points[0].X = left;
  1546. points[0].Y = top;
  1547. points[1].X = right;
  1548. points[1].Y = top;
  1549. points[2].X = right;
  1550. points[2].Y = bottom;
  1551. points[3].X = left;
  1552. points[3].Y = bottom;
  1553. const INT stackCount = 4;
  1554. GpPointF stackPoints[stackCount];
  1555. BYTE stackTypes[stackCount];
  1556. GpPath path(points,
  1557. 4,
  1558. stackPoints,
  1559. stackTypes,
  1560. stackCount,
  1561. FillModeAlternate,
  1562. DpPath::Convex);
  1563. if (path.IsValid())
  1564. {
  1565. DpRegion region(&path, matrix);
  1566. if (region.IsValid())
  1567. {
  1568. *isVisible = DeviceRegion.RegionVisible(&region);
  1569. return Ok;
  1570. }
  1571. }
  1572. }
  1573. }
  1574. *isVisible = FALSE;
  1575. return GenericError;
  1576. }
  1577. /**************************************************************************\
  1578. *
  1579. * Function Description:
  1580. *
  1581. * Determine if the specified region is inside or overlaps the region.
  1582. *
  1583. * Arguments:
  1584. *
  1585. * [IN] region - the region
  1586. * [IN] matrix - the world-to-device transformation matrix to use
  1587. * [OUT] isVisible - if the region is visible or not
  1588. *
  1589. * Return Value:
  1590. *
  1591. * GpStatus - Ok or failure status
  1592. *
  1593. * Created:
  1594. *
  1595. * 2/3/1999 DCurtis
  1596. *
  1597. \**************************************************************************/
  1598. GpStatus
  1599. GpRegion::IsVisible(
  1600. GpRegion * region,
  1601. GpMatrix * matrix,
  1602. BOOL * isVisible
  1603. ) const
  1604. {
  1605. ASSERT(IsValid());
  1606. ASSERT(matrix != NULL);
  1607. ASSERT((region != NULL) && (region->IsValid()));
  1608. if ((UpdateDeviceRegion(matrix) == Ok) &&
  1609. (region->UpdateDeviceRegion(matrix) == Ok))
  1610. {
  1611. *isVisible = DeviceRegion.RegionVisible(&(region->DeviceRegion));
  1612. return Ok;
  1613. }
  1614. *isVisible = FALSE;
  1615. return GenericError;
  1616. }
  1617. /**************************************************************************\
  1618. *
  1619. * Function Description:
  1620. *
  1621. * Determine if the region is empty, i.e. if it has no coverage area.
  1622. *
  1623. * Arguments:
  1624. *
  1625. * [IN] matrix - the world-to-device transformation matrix to use
  1626. * [OUT] isEmpty - if the region is empty or not
  1627. *
  1628. * Return Value:
  1629. *
  1630. * GpStatus - Ok or failure status
  1631. *
  1632. * Created:
  1633. *
  1634. * 01/06/1999 DCurtis
  1635. *
  1636. \**************************************************************************/
  1637. GpStatus
  1638. GpRegion::IsEmpty(
  1639. GpMatrix * matrix,
  1640. BOOL * isEmpty
  1641. ) const
  1642. {
  1643. ASSERT(IsValid());
  1644. ASSERT(matrix != NULL);
  1645. if (Type == TypeEmpty)
  1646. {
  1647. *isEmpty = TRUE;
  1648. return Ok;
  1649. }
  1650. if (UpdateDeviceRegion(matrix) == Ok)
  1651. {
  1652. *isEmpty = DeviceRegion.IsEmpty();
  1653. return Ok;
  1654. }
  1655. *isEmpty = FALSE;
  1656. return GenericError;
  1657. }
  1658. /**************************************************************************\
  1659. *
  1660. * Function Description:
  1661. *
  1662. * Determine if the region is infinite, i.e. if it has infinite coverage area.
  1663. *
  1664. * Arguments:
  1665. *
  1666. * [IN] matrix - the world-to-device transformation matrix to use
  1667. * [OUT] isInfinite - if the region is infinite or not
  1668. *
  1669. * Return Value:
  1670. *
  1671. * GpStatus - Ok or failure status
  1672. *
  1673. * Created:
  1674. *
  1675. * 01/06/1999 DCurtis
  1676. *
  1677. \**************************************************************************/
  1678. GpStatus
  1679. GpRegion::IsInfinite(
  1680. GpMatrix * matrix,
  1681. BOOL * isInfinite
  1682. ) const
  1683. {
  1684. ASSERT(IsValid());
  1685. ASSERT(matrix != NULL);
  1686. if (Type == TypeInfinite)
  1687. {
  1688. *isInfinite = TRUE;
  1689. return Ok;
  1690. }
  1691. // We have this here for cases like the following:
  1692. // This region was OR'ed with another region that was infinite.
  1693. // We wouldn't know this region was infinite now without checking
  1694. // the device region.
  1695. if (UpdateDeviceRegion(matrix) == Ok)
  1696. {
  1697. *isInfinite = DeviceRegion.IsInfinite();
  1698. return Ok;
  1699. }
  1700. *isInfinite = FALSE;
  1701. return GenericError;
  1702. }
  1703. /**************************************************************************\
  1704. *
  1705. * Function Description:
  1706. *
  1707. * Determine if the specified region is equal, in coverage area, to
  1708. * this region.
  1709. *
  1710. * Arguments:
  1711. *
  1712. * [IN] region - the region to check equality with
  1713. * [IN] matrix - the world-to-device transformation matrix to use
  1714. * [OUT] isEqual - if the regions are equal or not
  1715. *
  1716. * Return Value:
  1717. *
  1718. * GpStatus - Ok or failure status
  1719. *
  1720. * Created:
  1721. *
  1722. * 01/06/1999 DCurtis
  1723. *
  1724. \**************************************************************************/
  1725. GpStatus
  1726. GpRegion::IsEqual(
  1727. GpRegion * region,
  1728. GpMatrix * matrix,
  1729. BOOL * isEqual
  1730. ) const
  1731. {
  1732. ASSERT(IsValid());
  1733. ASSERT(matrix != NULL);
  1734. ASSERT((region != NULL) && (region->IsValid()));
  1735. if ((UpdateDeviceRegion(matrix) == Ok) &&
  1736. (region->UpdateDeviceRegion(matrix) == Ok))
  1737. {
  1738. *isEqual = DeviceRegion.IsEqual(&(region->DeviceRegion));
  1739. return Ok;
  1740. }
  1741. *isEqual = FALSE;
  1742. return GenericError;
  1743. }
  1744. /**************************************************************************\
  1745. *
  1746. * Function Description:
  1747. *
  1748. * Translate (offset) the region by the specified delta/offset values.
  1749. *
  1750. * Arguments:
  1751. *
  1752. * [IN] xOffset - amount to offset in X (world units)
  1753. * [IN] yOffset - amount to offset in Y
  1754. *
  1755. * Return Value:
  1756. *
  1757. * NONE
  1758. *
  1759. * Created:
  1760. *
  1761. * 01/06/1999 DCurtis
  1762. *
  1763. \**************************************************************************/
  1764. GpStatus
  1765. GpRegion::Offset(
  1766. REAL xOffset,
  1767. REAL yOffset
  1768. )
  1769. {
  1770. ASSERT(IsValid());
  1771. if ((xOffset == 0) && (yOffset == 0))
  1772. {
  1773. return Ok;
  1774. }
  1775. // Note that if performance is a problem, there's lots we could do here.
  1776. // For example, we could keep track of the offset, and only apply it
  1777. // when updating the device region. We could even avoid re-rasterizing
  1778. // the device region.
  1779. switch (Type)
  1780. {
  1781. case TypeEmpty:
  1782. case TypeInfinite:
  1783. return Ok; // do nothing
  1784. case TypeRect:
  1785. UpdateUid();
  1786. X += xOffset;
  1787. Y += yOffset;
  1788. break;
  1789. case TypePath:
  1790. UpdateUid();
  1791. if (Lazy)
  1792. {
  1793. Path = Path->Clone();
  1794. Lazy = FALSE;
  1795. if (Path == NULL)
  1796. {
  1797. Type = TypeNotValid;
  1798. return GenericError;
  1799. }
  1800. }
  1801. Path->Offset(xOffset, yOffset);
  1802. break;
  1803. default:
  1804. UpdateUid();
  1805. {
  1806. INT count = CombineData.GetCount();
  1807. RegionData * data = CombineData.GetDataBuffer();
  1808. NodeType type;
  1809. ASSERT ((count > 0) && (data != NULL));
  1810. do
  1811. {
  1812. type = data->Type;
  1813. if (type == TypeRect)
  1814. {
  1815. data->X += xOffset;
  1816. data->Y += yOffset;
  1817. }
  1818. else if (type == TypePath)
  1819. {
  1820. if (data->Lazy)
  1821. {
  1822. data->Path = data->Path->Clone();
  1823. data->Lazy = FALSE;
  1824. if (data->Path == NULL)
  1825. {
  1826. data->Type = TypeNotValid;
  1827. FreePathData();
  1828. Type = TypeNotValid;
  1829. return GenericError;
  1830. }
  1831. }
  1832. data->Path->Offset(xOffset, yOffset);
  1833. }
  1834. data++;
  1835. } while (--count > 0);
  1836. }
  1837. break;
  1838. }
  1839. if (RegionOk)
  1840. {
  1841. RegionOk = FALSE;
  1842. DeviceRegion.SetEmpty();
  1843. }
  1844. return Ok;
  1845. }
  1846. /**************************************************************************\
  1847. *
  1848. * Function Description:
  1849. *
  1850. * Transform a leaf node by the specified matrix. This could result in a
  1851. * rect being converted into a path. Ignores non-leaf nodes. No reason
  1852. * to transform empty/infinite nodes.
  1853. *
  1854. * Arguments:
  1855. *
  1856. * [IN] matrix - the transformation matrix to apply
  1857. * [IN/OUT] data - the node to transform
  1858. *
  1859. * Return Value:
  1860. *
  1861. * GpStatus - Ok or failure status
  1862. *
  1863. * Created:
  1864. *
  1865. * 02/08/1999 DCurtis
  1866. *
  1867. \**************************************************************************/
  1868. GpStatus
  1869. GpRegion::TransformLeaf(
  1870. GpMatrix * matrix,
  1871. RegionData * data
  1872. )
  1873. {
  1874. switch (data->Type)
  1875. {
  1876. // case TypeEmpty:
  1877. // case TypeInfinite:
  1878. // case TypeAnd, TypeOr, TypeXor, TypeExclude, TypeComplement:
  1879. default:
  1880. return Ok; // do nothing
  1881. case TypeRect:
  1882. {
  1883. if (matrix->IsTranslateScale())
  1884. {
  1885. GpRectF rect(data->X,
  1886. data->Y,
  1887. data->Width,
  1888. data->Height);
  1889. matrix->TransformRect(rect);
  1890. data->X = rect.X;
  1891. data->Y = rect.Y;
  1892. data->Width = rect.Width;
  1893. data->Height = rect.Height;
  1894. return Ok;
  1895. }
  1896. else
  1897. {
  1898. GpPath * path = new GpPath(FillModeAlternate);
  1899. if (path != NULL)
  1900. {
  1901. if (path->IsValid())
  1902. {
  1903. GpPointF points[4];
  1904. REAL left;
  1905. REAL right;
  1906. REAL top;
  1907. REAL bottom;
  1908. left = data->X;
  1909. top = data->Y;
  1910. right = data->X + data->Width;
  1911. bottom = data->Y + data->Height;
  1912. points[0].X = left;
  1913. points[0].Y = top;
  1914. points[1].X = right;
  1915. points[1].Y = top;
  1916. points[2].X = right;
  1917. points[2].Y = bottom;
  1918. points[3].X = left;
  1919. points[3].Y = bottom;
  1920. matrix->Transform(points, 4);
  1921. if (path->AddLines(points, 4) == Ok)
  1922. {
  1923. data->Path = path;
  1924. data->Lazy = FALSE;
  1925. data->Type = TypePath;
  1926. return Ok;
  1927. }
  1928. }
  1929. delete path;
  1930. }
  1931. data->Type = TypeNotValid;
  1932. }
  1933. }
  1934. return GenericError;
  1935. case TypePath:
  1936. if (data->Lazy)
  1937. {
  1938. data->Path = data->Path->Clone();
  1939. data->Lazy = FALSE;
  1940. if (data->Path == NULL)
  1941. {
  1942. data->Type = TypeNotValid;
  1943. return GenericError;
  1944. }
  1945. }
  1946. data->Path->Transform(matrix);
  1947. return Ok;
  1948. }
  1949. }
  1950. /**************************************************************************\
  1951. *
  1952. * Function Description:
  1953. *
  1954. * Transform a region with the specified matrix.
  1955. *
  1956. * Arguments:
  1957. *
  1958. * [IN] matrix - the transformation matrix to apply
  1959. *
  1960. * Return Value:
  1961. *
  1962. * GpStatus - Ok or failure status
  1963. *
  1964. * Created:
  1965. *
  1966. * 02/08/1999 DCurtis
  1967. *
  1968. \**************************************************************************/
  1969. GpStatus
  1970. GpRegion::Transform(
  1971. GpMatrix * matrix
  1972. )
  1973. {
  1974. ASSERT(IsValid());
  1975. if (matrix->IsIdentity() || (Type == TypeInfinite) || (Type == TypeEmpty))
  1976. {
  1977. return Ok;
  1978. }
  1979. UpdateUid();
  1980. if (RegionOk)
  1981. {
  1982. RegionOk = FALSE;
  1983. DeviceRegion.SetEmpty();
  1984. }
  1985. if ((Type & REGIONTYPE_LEAF) != 0)
  1986. {
  1987. return TransformLeaf(matrix, this);
  1988. }
  1989. else
  1990. {
  1991. BOOL error = FALSE;
  1992. INT count = CombineData.GetCount();
  1993. RegionData * data = CombineData.GetDataBuffer();
  1994. ASSERT((count > 0) && (data != NULL));
  1995. do
  1996. {
  1997. error |= (TransformLeaf(matrix, data++) != Ok);
  1998. } while (--count > 0);
  1999. if (!error)
  2000. {
  2001. return Ok;
  2002. }
  2003. }
  2004. FreePathData();
  2005. Type = TypeNotValid;
  2006. return GenericError;
  2007. }
  2008. class RegionRecordData : public ObjectData
  2009. {
  2010. public:
  2011. INT32 NodeCount;
  2012. };
  2013. GpStatus
  2014. GpRegion::SetData(
  2015. const BYTE * dataBuffer,
  2016. UINT size
  2017. )
  2018. {
  2019. if (dataBuffer == NULL)
  2020. {
  2021. WARNING(("dataBuffer is NULL"));
  2022. return InvalidParameter;
  2023. }
  2024. return this->Set(dataBuffer, size);
  2025. }
  2026. GpStatus
  2027. GpRegion::Set(
  2028. const BYTE * regionDataBuffer, // NULL means set to empty
  2029. UINT regionDataSize
  2030. )
  2031. {
  2032. GpStatus status = Ok;
  2033. if (regionDataBuffer != NULL)
  2034. {
  2035. if (regionDataSize < sizeof(RegionRecordData))
  2036. {
  2037. WARNING(("size too small"));
  2038. status = InsufficientBuffer;
  2039. goto SetEmptyRegion;
  2040. }
  2041. if (!((RegionRecordData *)regionDataBuffer)->MajorVersionMatches())
  2042. {
  2043. WARNING(("Version number mismatch"));
  2044. status = InvalidParameter;
  2045. goto SetEmptyRegion;
  2046. }
  2047. UpdateUid();
  2048. if (RegionOk)
  2049. {
  2050. RegionOk = FALSE;
  2051. DeviceRegion.SetEmpty();
  2052. }
  2053. FreePathData();
  2054. RegionData * regionDataArray = NULL;
  2055. INT nodeCount = ((RegionRecordData *)regionDataBuffer)->NodeCount;
  2056. if (nodeCount > 0)
  2057. {
  2058. regionDataArray = CombineData.AddMultiple(nodeCount);
  2059. if (regionDataArray == NULL)
  2060. {
  2061. Type = TypeNotValid;
  2062. return OutOfMemory;
  2063. }
  2064. }
  2065. regionDataBuffer += sizeof(RegionRecordData);
  2066. regionDataSize -= sizeof(RegionRecordData);
  2067. INT nextArrayIndex = 0;
  2068. status = SetRegionData(regionDataBuffer, regionDataSize,
  2069. this, regionDataArray,
  2070. nextArrayIndex, nodeCount);
  2071. if (status == Ok)
  2072. {
  2073. ASSERT(nextArrayIndex == nodeCount);
  2074. return Ok;
  2075. }
  2076. Type = TypeNotValid;
  2077. return status;
  2078. }
  2079. SetEmptyRegion:
  2080. SetEmpty();
  2081. return status;
  2082. }
  2083. GpStatus
  2084. GpRegion::SetRegionData(
  2085. const BYTE * & regionDataBuffer,
  2086. UINT & regionDataSize,
  2087. RegionData * regionData,
  2088. RegionData * regionDataArray,
  2089. INT & nextArrayIndex,
  2090. INT arraySize
  2091. )
  2092. {
  2093. for (;;)
  2094. {
  2095. if (regionDataSize < sizeof(INT32))
  2096. {
  2097. WARNING(("size too small"));
  2098. return InsufficientBuffer;
  2099. }
  2100. regionData->Type = (NodeType)(((INT32 *)regionDataBuffer)[0]);
  2101. regionDataBuffer += sizeof(INT32);
  2102. regionDataSize -= sizeof(INT32);
  2103. if ((regionData->Type & REGIONTYPE_LEAF) != 0)
  2104. {
  2105. switch (regionData->Type)
  2106. {
  2107. case TypeRect:
  2108. if (regionDataSize < (4 * sizeof(REAL)))
  2109. {
  2110. WARNING(("size too small"));
  2111. return InsufficientBuffer;
  2112. }
  2113. regionData->X = ((REAL *)regionDataBuffer)[0];
  2114. regionData->Y = ((REAL *)regionDataBuffer)[1];
  2115. regionData->Width = ((REAL *)regionDataBuffer)[2];
  2116. regionData->Height = ((REAL *)regionDataBuffer)[3];
  2117. regionDataBuffer += (4 * sizeof(REAL));
  2118. regionDataSize -= (4 * sizeof(REAL));
  2119. break;
  2120. case TypePath:
  2121. {
  2122. if (regionDataSize < sizeof(INT32))
  2123. {
  2124. WARNING(("size too small"));
  2125. return InsufficientBuffer;
  2126. }
  2127. GpPath * path = new GpPath();
  2128. UINT pathSize = ((INT32 *)regionDataBuffer)[0];
  2129. regionDataBuffer += sizeof(INT32);
  2130. regionDataSize -= sizeof(INT32);
  2131. if (path == NULL)
  2132. {
  2133. return OutOfMemory;
  2134. }
  2135. UINT tmpPathSize = pathSize;
  2136. if ((path->SetData(regionDataBuffer, tmpPathSize) != Ok) ||
  2137. (!path->IsValid()))
  2138. {
  2139. delete path;
  2140. return InvalidParameter;
  2141. }
  2142. regionDataBuffer += pathSize;
  2143. regionDataSize -= pathSize;
  2144. regionData->Path = path;
  2145. regionData->Lazy = FALSE;
  2146. }
  2147. break;
  2148. case TypeEmpty:
  2149. case TypeInfinite:
  2150. break;
  2151. default:
  2152. ASSERT(0);
  2153. break;
  2154. }
  2155. break; // get out of loop
  2156. }
  2157. else // it's not a leaf node
  2158. {
  2159. if ((regionDataArray == NULL) ||
  2160. (nextArrayIndex >= arraySize))
  2161. {
  2162. ASSERT(0);
  2163. return InvalidParameter;
  2164. }
  2165. regionData->Left = nextArrayIndex++;
  2166. // traverse left
  2167. GpStatus status = SetRegionData(regionDataBuffer,
  2168. regionDataSize,
  2169. regionDataArray + regionData->Left,
  2170. regionDataArray,
  2171. nextArrayIndex,
  2172. arraySize);
  2173. if (status != Ok)
  2174. {
  2175. return status;
  2176. }
  2177. if (nextArrayIndex >= arraySize)
  2178. {
  2179. ASSERT(0);
  2180. return InvalidParameter;
  2181. }
  2182. regionData->Right = nextArrayIndex++;
  2183. // traverse right using tail-end recursion
  2184. regionData = regionDataArray + regionData->Right;
  2185. }
  2186. }
  2187. return Ok;
  2188. }
  2189. /**************************************************************************\
  2190. *
  2191. * Function Description:
  2192. *
  2193. * Serialize all the region data into a single memory buffer. If the
  2194. * buffer is NULL, just return the number of bytes required in the buffer.
  2195. *
  2196. * Arguments:
  2197. *
  2198. * [IN] regionDataBuffer - the memory buffer to fill with region data
  2199. *
  2200. * Return Value:
  2201. *
  2202. * INT - Num Bytes required (or used) to fill with region data
  2203. *
  2204. * Created:
  2205. *
  2206. * 09/01/1999 DCurtis
  2207. *
  2208. \**************************************************************************/
  2209. GpStatus
  2210. GpRegion::GetData(
  2211. IStream * stream
  2212. ) const
  2213. {
  2214. ASSERT (stream != NULL);
  2215. RegionRecordData regionRecordData;
  2216. regionRecordData.NodeCount = CombineData.GetCount();
  2217. stream->Write(&regionRecordData, sizeof(regionRecordData), NULL);
  2218. return this->GetRegionData(stream, this);
  2219. }
  2220. UINT
  2221. GpRegion::GetDataSize() const
  2222. {
  2223. return sizeof(RegionRecordData) + this->GetRegionDataSize(this);
  2224. }
  2225. /**************************************************************************\
  2226. *
  2227. * Function Description:
  2228. *
  2229. * Recurse through the region data structure to determine how many bytes
  2230. * are required to hold all the region data.
  2231. *
  2232. * Arguments:
  2233. *
  2234. * [IN] regionData - the region data node to start with
  2235. *
  2236. * Return Value:
  2237. *
  2238. * INT - the size in bytes from this node down
  2239. *
  2240. * Created:
  2241. *
  2242. * 09/01/1999 DCurtis
  2243. *
  2244. \**************************************************************************/
  2245. INT
  2246. GpRegion::GetRegionDataSize(
  2247. const RegionData * regionData
  2248. ) const
  2249. {
  2250. INT size = 0;
  2251. for (;;)
  2252. {
  2253. size += sizeof(INT32); // for the type of this node
  2254. if ((regionData->Type & REGIONTYPE_LEAF) != 0)
  2255. {
  2256. switch (regionData->Type)
  2257. {
  2258. case TypeRect:
  2259. size += (4 * sizeof(REAL)); // for the rect data
  2260. break;
  2261. case TypePath:
  2262. size += sizeof(INT32) + regionData->Path->GetDataSize();
  2263. ASSERT((size & 0x03) == 0);
  2264. break;
  2265. case TypeEmpty:
  2266. case TypeInfinite:
  2267. break;
  2268. default:
  2269. ASSERT(0);
  2270. break;
  2271. }
  2272. break; // get out of loop
  2273. }
  2274. else // it's not a leaf node
  2275. {
  2276. // traverse left
  2277. size += GetRegionDataSize(&(CombineData[regionData->Left]));
  2278. // traverse right using tail-end recursion
  2279. regionData = &(CombineData[regionData->Right]);
  2280. }
  2281. }
  2282. return size;
  2283. }
  2284. /**************************************************************************\
  2285. *
  2286. * Function Description:
  2287. *
  2288. * Recurse through the region data structure writing each region data
  2289. * node to the memory buffer.
  2290. *
  2291. * Arguments:
  2292. *
  2293. * [IN] regionData - the region data node to start with
  2294. * [IN] regionDataBuffer - the memory buffer to write the data to
  2295. *
  2296. * Return Value:
  2297. *
  2298. * BYTE * - the next memory location to write to
  2299. *
  2300. * Created:
  2301. *
  2302. * 09/01/1999 DCurtis
  2303. *
  2304. \**************************************************************************/
  2305. GpStatus
  2306. GpRegion::GetRegionData(
  2307. IStream * stream,
  2308. const RegionData * regionData
  2309. ) const
  2310. {
  2311. ASSERT(stream != NULL);
  2312. GpStatus status = Ok;
  2313. UINT pathSize;
  2314. REAL rectBuffer[4];
  2315. for (;;)
  2316. {
  2317. stream->Write(&regionData->Type, sizeof(INT32), NULL);
  2318. if ((regionData->Type & REGIONTYPE_LEAF) != 0)
  2319. {
  2320. switch (regionData->Type)
  2321. {
  2322. case TypeRect:
  2323. rectBuffer[0] = regionData->X;
  2324. rectBuffer[1] = regionData->Y;
  2325. rectBuffer[2] = regionData->Width;
  2326. rectBuffer[3] = regionData->Height;
  2327. stream->Write(rectBuffer, 4 * sizeof(rectBuffer[0]), NULL);
  2328. break;
  2329. case TypePath:
  2330. pathSize = regionData->Path->GetDataSize();
  2331. ASSERT((pathSize & 0x03) == 0);
  2332. stream->Write(&pathSize, sizeof(INT32), NULL);
  2333. status = regionData->Path->GetData(stream);
  2334. break;
  2335. case TypeEmpty:
  2336. case TypeInfinite:
  2337. break;
  2338. default:
  2339. ASSERT(0);
  2340. break;
  2341. }
  2342. break; // get out of loop
  2343. }
  2344. else // it's not a leaf node
  2345. {
  2346. // traverse left
  2347. status = GetRegionData(stream, &(CombineData[regionData->Left]));
  2348. // traverse right using tail-end recursion
  2349. regionData = &(CombineData[regionData->Right]);
  2350. }
  2351. if (status != Ok)
  2352. {
  2353. break;
  2354. }
  2355. }
  2356. return status;
  2357. }
  2358. /**************************************************************************\
  2359. *
  2360. * Function Description:
  2361. *
  2362. * Constructor. Sets the region to a copy of the specified path.
  2363. *
  2364. * Arguments:
  2365. *
  2366. * [IN] path - path to initialize the region to
  2367. *
  2368. * Return Value:
  2369. *
  2370. * NONE
  2371. *
  2372. * Created:
  2373. *
  2374. * 2/3/1999 DCurtis
  2375. *
  2376. \**************************************************************************/
  2377. GpRegion::GpRegion(
  2378. HRGN hRgn
  2379. )
  2380. {
  2381. ASSERT(hRgn != NULL);
  2382. SetValid(TRUE); // default is valid
  2383. RegionOk = FALSE;
  2384. Lazy = FALSE;
  2385. Type = TypeNotValid;
  2386. Path = new GpPath(hRgn);
  2387. if (CheckValid(Path))
  2388. {
  2389. Type = TypePath;
  2390. }
  2391. }