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.

680 lines
20 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1998 Microsoft Corporation
  4. *
  5. * Module Name:
  6. *
  7. * DpRegion.hpp
  8. *
  9. * Abstract:
  10. *
  11. * DpRegion class operates on scan-converted Y spans of rects
  12. *
  13. * Created:
  14. *
  15. * 12/16/1998 DCurtis
  16. *
  17. \**************************************************************************/
  18. #ifndef _DPREGION_HPP
  19. #define _DPREGION_HPP
  20. // Each Y span consists of 4 values
  21. #define YSPAN_SIZE 4
  22. #define YSPAN_YMIN 0 // minimum Y value (inclusive)
  23. #define YSPAN_YMAX 1 // maximum Y value (exclusive)
  24. #define YSPAN_XOFFSET 2 // offset into XCoords for this span
  25. #define YSPAN_XCOUNT 3 // num XCoords for this span (multiple of 2)
  26. #if 0
  27. // The infinite max and min values are set up to be the greatest
  28. // values that will interop with GDI HRGNs on WinNT successfully (divided by 2).
  29. // We divide those values by 2 so that there is a little room to offset
  30. // the GDI region and still have it work correctly.
  31. // Of course, this won't work on Win9x -- we have to handle Win9x as
  32. // a special case in the GetHRGN code.
  33. #define INFINITE_MAX 0x03FFFFC7 // (0x07ffff8f / 2)
  34. #define INFINITE_MIN 0xFC000038 // (0xF8000070 / 2)
  35. #define INFINITE_SIZE (INFINITE_MAX - INFINITE_MIN)
  36. #else
  37. // Instead of the above, let's use the largest possible integer value
  38. // that is guaranteed to round trip correctly between float and int,
  39. // but as above, let's leave a little head room so we can offset the region.
  40. #include "float.h"
  41. #define INFINITE_MAX (1 << (FLT_MANT_DIG - 2))
  42. #define INFINITE_MIN (-INFINITE_MAX)
  43. #define INFINITE_SIZE (INFINITE_MAX - INFINITE_MIN)
  44. #endif
  45. class DpComplexRegion
  46. {
  47. public:
  48. INT XCoordsCapacity; // num XCoords (INTs) allocated
  49. INT XCoordsCount; // num XCoords (INTs) used
  50. INT YSpansCapacity; // num YSpans allocated
  51. INT NumYSpans; // NumYSpans used
  52. INT YSearchIndex; // search start index in YSpans
  53. INT * XCoords; // pointer to XCoords
  54. INT * YSpans; // pointer to YSpans
  55. // (XCoords + XCoordsCapacity)
  56. public:
  57. INT * GetYSpan (INT spanIndex)
  58. {
  59. return YSpans + (spanIndex * YSPAN_SIZE);
  60. }
  61. VOID ResetSearchIndex()
  62. {
  63. YSearchIndex = NumYSpans >> 1;
  64. }
  65. BOOL YSpanSearch(INT y, INT ** ySpan, INT * spanIndex);
  66. };
  67. class DpRegionBuilder : public GpOutputYSpan
  68. {
  69. private:
  70. // We now use an ObjectTag to determine if the object is valid
  71. // instead of using a BOOL. This is much more robust and helps
  72. // with debugging. It also enables us to version our objects
  73. // more easily with a version number in the ObjectTag.
  74. ObjectTag Tag; // Keep this as the 1st value in the object!
  75. protected:
  76. VOID SetValid(BOOL valid)
  77. {
  78. Tag = valid ? ObjectTagDpRegionBuilder : ObjectTagInvalid;
  79. }
  80. public:
  81. INT XMin;
  82. INT YMin;
  83. INT XMax;
  84. INT YMax;
  85. DpComplexRegion * ComplexData;
  86. public:
  87. DpRegionBuilder(INT ySpans)
  88. {
  89. SetValid(InitComplexData(ySpans) == Ok);
  90. }
  91. ~DpRegionBuilder()
  92. {
  93. GpFree(ComplexData);
  94. SetValid(FALSE); // so we don't use a deleted object
  95. }
  96. BOOL IsValid() const
  97. {
  98. ASSERT((Tag == ObjectTagDpRegionBuilder) || (Tag == ObjectTagInvalid));
  99. #if DBG
  100. if (Tag == ObjectTagInvalid)
  101. {
  102. WARNING1("Invalid DpRegionBuilder");
  103. }
  104. #endif
  105. return (Tag == ObjectTagDpRegionBuilder);
  106. }
  107. virtual GpStatus OutputYSpan(
  108. INT yMin,
  109. INT yMax,
  110. INT * xCoords, // even number of X coordinates
  111. INT numXCoords // must be a multiple of 2
  112. );
  113. GpStatus InitComplexData(INT ySpans);
  114. };
  115. // Forward Declaration for friendliness
  116. class DpClipRegion;
  117. // This class was constructed to optimize for space and speed for the common
  118. // case of the region consisting of a single rectangle. For that case of a
  119. // simple region, we want the region to be as small and as fast as possible so
  120. // that it is not expensive to have a region associated with every window.
  121. class DpRegion
  122. {
  123. private:
  124. friend DpClipRegion;
  125. // We now use an ObjectTag to determine if the object is valid
  126. // instead of using a BOOL. This is much more robust and helps
  127. // with debugging. It also enables us to version our objects
  128. // more easily with a version number in the ObjectTag.
  129. ObjectTag Tag; // Keep this as the 1st value in the object!
  130. protected:
  131. VOID SetValid(BOOL valid)
  132. {
  133. Tag = valid ? ObjectTagDpRegion : ObjectTagInvalid;
  134. }
  135. protected:
  136. UINT32 Infinite : 1;
  137. UINT32 Empty : 1;
  138. UINT32 Lazy : 1;
  139. UINT32 Pad : 29;
  140. INT XMin;
  141. INT YMin;
  142. INT XMax; // exclusive
  143. INT YMax; // exclusive
  144. DpComplexRegion * ComplexData;
  145. mutable INT Uniqueness;
  146. public:
  147. enum Visibility
  148. {
  149. Invisible = 0,
  150. PartiallyVisible = 1,
  151. ClippedVisible = 2,
  152. TotallyVisible = 3,
  153. };
  154. public:
  155. DpRegion(BOOL empty = FALSE); // default is infinite region
  156. DpRegion(const GpRect * rect);
  157. DpRegion(INT x, INT y, INT width, INT height);
  158. DpRegion(const DpPath * path, const GpMatrix * matrix);
  159. DpRegion(const DpRegion * region, BOOL lazy = FALSE);
  160. DpRegion(DpRegion & region); // copy constructor
  161. DpRegion(const RECT * rects, INT count);
  162. ~DpRegion()
  163. {
  164. FreeData();
  165. SetValid(FALSE); // so we don't use a deleted object
  166. }
  167. DpRegion &operator=( DpRegion & region );
  168. BOOL IsValid() const
  169. {
  170. ASSERT((Tag == ObjectTagDpRegion) || (Tag == ObjectTagInvalid));
  171. #if DBG
  172. if (Tag == ObjectTagInvalid)
  173. {
  174. WARNING1("Invalid DpRegion");
  175. }
  176. #endif
  177. return (Tag == ObjectTagDpRegion);
  178. }
  179. VOID Set(INT x, INT y, INT width, INT height);
  180. VOID Set(GpRect * rect)
  181. {
  182. ASSERT (rect != NULL);
  183. Set(rect->X, rect->Y, rect->Width, rect->Height);
  184. }
  185. GpStatus Set(const DpPath * path, const GpMatrix * matrix); // path is in world units
  186. GpStatus Set(const DpRegion * region, BOOL lazy = FALSE);
  187. GpStatus Set(const RECT * rects, INT count);
  188. VOID SetInfinite();
  189. VOID SetEmpty();
  190. GpStatus Offset(INT xOffset, INT yOffset);
  191. GpStatus And (const DpRegion * region);
  192. GpStatus Or (const DpRegion * region);
  193. GpStatus Xor (const DpRegion * region);
  194. GpStatus Exclude (const DpRegion * region);
  195. GpStatus Complement(const DpRegion * region);
  196. INT GetXMin() const { ASSERT(IsValid()); return XMin; }
  197. INT GetYMin() const { ASSERT(IsValid()); return YMin; }
  198. INT GetXMax() const { ASSERT(IsValid()); return XMax; }
  199. INT GetYMax() const { ASSERT(IsValid()); return YMax; }
  200. INT GetUniqueness() const
  201. { ASSERT(IsValid());
  202. if (Uniqueness == 0)
  203. {
  204. Uniqueness = GenerateUniqueness();
  205. }
  206. return Uniqueness;
  207. }
  208. VOID UpdateUID() { ASSERT(IsValid()); Uniqueness = 0; }
  209. VOID GetBounds(GpRect * bounds) const
  210. {
  211. ASSERT(IsValid());
  212. ASSERT(bounds != NULL);
  213. bounds->X = XMin;
  214. bounds->Y = YMin;
  215. bounds->Width = XMax - XMin;
  216. bounds->Height = YMax - YMin;
  217. }
  218. VOID GetBounds(GpPointF * topLeft, GpPointF * bottomRight) const
  219. {
  220. ASSERT(IsValid());
  221. ASSERT((topLeft != NULL) && (bottomRight != NULL));
  222. topLeft->X = (REAL)XMin;
  223. topLeft->Y = (REAL)YMin;
  224. bottomRight->X = (REAL)XMax;
  225. bottomRight->Y = (REAL)YMax;
  226. }
  227. BOOL IsEqual(DpRegion * region);
  228. BOOL IsEmpty() const
  229. {
  230. ASSERT(IsValid());
  231. return Empty;
  232. }
  233. BOOL IsInfinite() const
  234. {
  235. ASSERT(IsValid());
  236. return Infinite;
  237. }
  238. // Empty and Infinite regions are always simple as well.
  239. BOOL IsSimple() const
  240. {
  241. ASSERT(IsValid());
  242. return (ComplexData == NULL);
  243. }
  244. BOOL IsComplex() const
  245. {
  246. return !IsSimple();
  247. }
  248. Visibility GetRectVisibility(
  249. INT xMin,
  250. INT yMin,
  251. INT xMax,
  252. INT yMax,
  253. GpRect * rectClipped = NULL
  254. );
  255. BOOL RegionVisible (DpRegion * region);
  256. BOOL RectVisible(INT xMin, INT yMin, INT xMax, INT yMax);
  257. BOOL RectVisible(GpRect * rect)
  258. {
  259. return RectVisible(rect->X, rect->Y,
  260. rect->X + rect->Width, rect->Y + rect->Height);
  261. }
  262. BOOL RectInside (INT xMin, INT yMin, INT xMax, INT yMax);
  263. BOOL PointInside(INT x, INT y);
  264. GpStatus Fill(DpOutputSpan * output, GpRect * clipBounds) const;
  265. HRGN GetHRgn() const;
  266. BOOL GetOutlinePoints(DynPointArray& points,
  267. DynByteArray& types) const;
  268. // If rects is NULL, return the number of rects in the region.
  269. // Otherwise, assume rects is big enough to hold all the region rects
  270. // and fill them in and return the number of rects filled in.
  271. INT GetRects(GpRect * rects) const;
  272. INT GetRects(GpRectF * rects) const;
  273. protected:
  274. VOID FreeData()
  275. {
  276. if (!Lazy)
  277. {
  278. GpFree(ComplexData);
  279. }
  280. ComplexData = NULL;
  281. Lazy = FALSE;
  282. }
  283. GpStatus Set(DpRegionBuilder & regionBuilder);
  284. // If rects is NULL, return the number of rects in the region.
  285. // Otherwise, assume rects is big enough to hold all the region rects
  286. // and fill them in and return the number of rects filled in.
  287. INT GetRects(RECT * rects, BOOL clampToWin9xSize = FALSE) const;
  288. GpStatus CompactAndOutput(
  289. INT yMin,
  290. INT yMax,
  291. INT * xCoords,
  292. INT numXCoords,
  293. DpRegionBuilder * regionBuilder,
  294. DynIntArray * combineCoords
  295. );
  296. GpStatus Diff(
  297. DpRegion * region1,
  298. DpRegion * region2,
  299. BOOL set1
  300. );
  301. GpStatus XSpansAND (
  302. DynIntArray * combineCoords,
  303. INT * xSpan1,
  304. INT numXCoords1,
  305. INT * xSpan2,
  306. INT numXCoords2
  307. );
  308. GpStatus XSpansOR (
  309. DynIntArray * combineCoords,
  310. INT * xSpan1,
  311. INT numXCoords1,
  312. INT * xSpan2,
  313. INT numXCoords2
  314. );
  315. GpStatus XSpansXOR (
  316. DynIntArray * combineCoords,
  317. INT * xSpan1,
  318. INT numXCoords1,
  319. INT * xSpan2,
  320. INT numXCoords2
  321. );
  322. GpStatus XSpansDIFF(
  323. DynIntArray * combineCoords,
  324. INT * xSpan1,
  325. INT numXCoords1,
  326. INT * xSpan2,
  327. INT numXCoords2
  328. );
  329. private:
  330. static INT
  331. GenerateUniqueness(
  332. )
  333. {
  334. LONG_PTR Uid;
  335. static LONG_PTR gUniqueness = 0 ;
  336. // Use InterlockedCompareExchangeFunction instead of
  337. // InterlockedIncrement, because InterlockedIncrement doesn't work
  338. // the way we need it to on Win9x.
  339. do
  340. {
  341. Uid = gUniqueness;
  342. } while (CompareExchangeLong_Ptr(&gUniqueness, (Uid + 1), Uid) != Uid);
  343. return (INT) (Uid + 1);
  344. }
  345. };
  346. class DpClipRegion : public DpRegion,
  347. public DpOutputSpan
  348. {
  349. public:
  350. enum Direction
  351. {
  352. NotEnumerating,
  353. TopLeftToBottomRight,
  354. TopRightToBottomLeft,
  355. BottomLeftToTopRight,
  356. BottomRightToTopLeft
  357. };
  358. protected:
  359. DpOutputSpan * OutputClippedSpan;
  360. DpRegion OriginalRegion; // The old Region
  361. BOOL ComplexClipDisabled; // Did we disable the complexClip
  362. // enumeration stuff
  363. Direction EnumDirection;
  364. INT EnumSpan;
  365. private:
  366. friend class DriverPrint;
  367. // This is a special method intended for use only by DriverPrint.
  368. VOID StartBanding()
  369. {
  370. ASSERT(IsValid());
  371. ASSERT(!Empty); // if clipping is empty, we shouldn't be banding
  372. ASSERT(ComplexClipDisabled == FALSE); // Unless we are banding we can't
  373. // have ComplexClip disabled
  374. ASSERT(OriginalRegion.IsEmpty());
  375. // Save the current information of the ClipRegion into OriginalRegion
  376. // this is done by setting the complexData to our current complexData
  377. ASSERT(OriginalRegion.ComplexData == NULL);
  378. OriginalRegion.ComplexData = ComplexData;
  379. OriginalRegion.Lazy = Lazy;
  380. OriginalRegion.Uniqueness = Uniqueness;
  381. OriginalRegion.Infinite = Infinite;
  382. OriginalRegion.Empty = Empty;
  383. OriginalRegion.XMin = XMin;
  384. OriginalRegion.YMin = YMin;
  385. OriginalRegion.XMax = XMax;
  386. OriginalRegion.YMax = YMax;
  387. // We don't want to both regions to point to the same data
  388. ComplexData = NULL;
  389. Lazy = FALSE;
  390. // Create a lazy region so that we don't copy unless needed
  391. Set(&OriginalRegion, TRUE);
  392. }
  393. // This is a special method intended for use only by DriverPrint.
  394. VOID EndBanding()
  395. {
  396. ASSERT(IsValid());
  397. ASSERT(!OriginalRegion.IsEmpty());
  398. ASSERT(!ComplexClipDisabled); // Make sure that we don't leave an opened
  399. // DisableComplexClip call
  400. // Free our Data, we can't be Lazy
  401. FreeData();
  402. ASSERT(ComplexData == NULL);
  403. ComplexData = OriginalRegion.ComplexData;
  404. Lazy = OriginalRegion.Lazy;
  405. Uniqueness = OriginalRegion.Uniqueness;
  406. Infinite = OriginalRegion.Infinite;
  407. Empty = OriginalRegion.Empty;
  408. XMin = OriginalRegion.XMin;
  409. YMin = OriginalRegion.YMin;
  410. XMax = OriginalRegion.XMax;
  411. YMax = OriginalRegion.YMax;
  412. // We don't want to both regions to point to the same data
  413. OriginalRegion.ComplexData = NULL;
  414. OriginalRegion.Lazy = FALSE;
  415. OriginalRegion.SetEmpty();
  416. }
  417. // This is a special method intended for use only by DriverPrint.
  418. // We're relying on DriverPrint to restore the bounds back correctly
  419. // after it's done with banding (by bracketing the banding with
  420. // StartBanding and EndBanding calls).
  421. // This works, even when there is complex clipping, because OutputSpan
  422. // clips against the bounds before clipping against the complex region.
  423. VOID SetBandBounds(GpRect & bandBounds, BOOL doIntersect = TRUE)
  424. {
  425. ASSERT(IsValid());
  426. ASSERT(!OriginalRegion.IsEmpty());
  427. ASSERT((bandBounds.Width > 0) && (bandBounds.Height > 0));
  428. GpRect intersectedBounds;
  429. if (!doIntersect)
  430. {
  431. // We are coming from DisableComplexClipping. We should not have
  432. // any ComplexData.
  433. ASSERT(!Lazy);
  434. ASSERT(ComplexData == NULL);
  435. // The reason for not doing the intersection is that sometimes
  436. // printing using a capped DPI which means that the bandBounds is
  437. // using a different coordinate system than the original region.
  438. intersectedBounds = bandBounds;
  439. }
  440. else
  441. {
  442. GpRect boundsOriginal(OriginalRegion.XMin, OriginalRegion.YMin,
  443. OriginalRegion.XMax - OriginalRegion.XMin,
  444. OriginalRegion.YMax - OriginalRegion.YMin);
  445. if (!GpRect::IntersectRect(intersectedBounds, bandBounds, boundsOriginal))
  446. {
  447. // intersection is empty
  448. SetEmpty();
  449. return;
  450. }
  451. }
  452. // Create a region for our band
  453. DpRegion newRegion(&intersectedBounds);
  454. // If we haven't disabled the complex clipping, then restore the original
  455. // complexRegion and intersect with our current region
  456. if (!ComplexClipDisabled)
  457. {
  458. // Get the original Clipping Region
  459. Set(&OriginalRegion, TRUE);
  460. // Intersect with our current band
  461. And(&newRegion);
  462. }
  463. else
  464. {
  465. // We've disabled the complexClipping, Set the clipping to our band
  466. Set(&newRegion);
  467. ASSERT(ComplexData == NULL);
  468. }
  469. }
  470. // This is a special method intended for use only by DriverPrint.
  471. // We're relying on DriverPrint to re-enable the complex clipping
  472. // and to restore the bounds back correctly after it's done with banding
  473. // (by bracketing the banding with StartBanding and EndBanding calls).
  474. // Typically, when we disable complex clipping, it's because we are
  475. // using a capped DPI, which means that the bandBounds is in a different
  476. // resolution than the original clipping region.
  477. VOID DisableComplexClipping(GpRect & bandBounds, BOOL doIntersect = FALSE)
  478. {
  479. ASSERT(IsValid());
  480. ASSERT(!OriginalRegion.IsEmpty());
  481. // Make sure that we call ourselves twice in a row
  482. ASSERT(ComplexClipDisabled == FALSE);
  483. ComplexClipDisabled = TRUE;
  484. Set(&bandBounds);
  485. ASSERT(ComplexData == NULL);
  486. SetBandBounds(bandBounds, doIntersect);
  487. }
  488. // This is a special method intended for use only by DriverPrint.
  489. VOID ReEnableComplexClipping()
  490. {
  491. ASSERT(IsValid());
  492. ASSERT(!OriginalRegion.IsEmpty());
  493. ASSERT(ComplexClipDisabled);
  494. ComplexClipDisabled = FALSE;
  495. // Set the clipping back to the original state, make in Lazy
  496. Set(&OriginalRegion, TRUE);
  497. }
  498. public:
  499. DpClipRegion(BOOL empty = FALSE) : DpRegion(empty)
  500. {
  501. OutputClippedSpan = NULL;
  502. ComplexClipDisabled = FALSE;
  503. #if DBG
  504. OriginalRegion.SetEmpty();
  505. #endif
  506. }
  507. DpClipRegion(const GpRect * rect) : DpRegion(rect)
  508. {
  509. OutputClippedSpan = NULL;
  510. ComplexClipDisabled = FALSE;
  511. #if DBG
  512. OriginalRegion.SetEmpty();
  513. #endif
  514. }
  515. DpClipRegion(INT x, INT y, INT width, INT height) :
  516. DpRegion(x, y, width, height)
  517. {
  518. OutputClippedSpan = NULL;
  519. ComplexClipDisabled = FALSE;
  520. #if DBG
  521. OriginalRegion.SetEmpty();
  522. #endif
  523. }
  524. DpClipRegion(const DpPath * path, const GpMatrix * matrix) : DpRegion (path, matrix)
  525. {
  526. OutputClippedSpan = NULL;
  527. ComplexClipDisabled = FALSE;
  528. #if DBG
  529. OriginalRegion.SetEmpty();
  530. #endif
  531. }
  532. DpClipRegion(const DpRegion * region) : DpRegion(region)
  533. {
  534. OutputClippedSpan = NULL;
  535. ComplexClipDisabled = FALSE;
  536. #if DBG
  537. OriginalRegion.SetEmpty();
  538. #endif
  539. }
  540. DpClipRegion(DpClipRegion & region) : DpRegion(region)
  541. {
  542. OutputClippedSpan = NULL;
  543. ComplexClipDisabled = FALSE;
  544. #if DBG
  545. OriginalRegion.SetEmpty();
  546. #endif
  547. }
  548. VOID StartEnumeration (
  549. INT yMin,
  550. Direction direction = TopLeftToBottomRight
  551. );
  552. // returns FALSE when done enumerating
  553. // numRects going in is the number of rects in the buffer and going out
  554. // is the number of rects that we filled.
  555. BOOL Enumerate (
  556. GpRect * rects,
  557. INT & numRects
  558. );
  559. virtual GpStatus OutputSpan(
  560. INT y,
  561. INT xMin,
  562. INT xMax
  563. );
  564. VOID InitClipping(DpOutputSpan * outputClippedSpan, INT yMin);
  565. // EndClipping is here only for debugging (and the Rasterizer doesn't
  566. // call it):
  567. VOID EndClipping() {}
  568. virtual BOOL IsValid() const { return TRUE; }
  569. };
  570. #endif // _DPREGION_HPP