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.

522 lines
22 KiB

  1. /**************************************************************************\
  2. * *
  3. * Copyright (c) 1998 Microsoft Corporation *
  4. * *
  5. * Module Name: *
  6. * *
  7. * Region to Path Conversion class. *
  8. * *
  9. * Abstract: *
  10. * *
  11. * Code from Kirk Olynyk [kirko] created 14-Sep-1993. This code will *
  12. * convert rectangular regions to a path by analyzing the DDA pattern. *
  13. * *
  14. * Discussion: *
  15. * *
  16. * Input *
  17. * *
  18. * The input to the diagonalization routing is a rectangular *
  19. * path whose vertices have integer endpoints. Moreover it *
  20. * is required that the path always has the region on its *
  21. * left and that successive lines are mutually orthogonal. *
  22. * *
  23. * All paths are in device 28.4 coordinates. (Since all of *
  24. * the input coordinates are integers, the fractional part of all *
  25. * coordinates is zero.) *
  26. * *
  27. * Output *
  28. * *
  29. * A path that contains the same pixels as the originl path. *
  30. * *
  31. * Filling Convention *
  32. * *
  33. * Any region bounded by two non-horizontal lines is closed *
  34. * on the left and open on the right. If the region is bounded *
  35. * by two horizontal lines, it is closed on the top and open on *
  36. * bottom. *
  37. * *
  38. * Definition *
  39. * *
  40. * A CORNER is subsequence of two lines from the orignal axial path. *
  41. * It is convenient to partition the set of corners into two classes; *
  42. * HORIZONTAL-VERTIAL and VERTICAL-HORIZONTAL. *
  43. * *
  44. * A corner is "diagonalizable" the original two lines can be replaced *
  45. * by a single diagonal line such that same pixels would be rendered *
  46. * (using the filling convention defined above). *
  47. * *
  48. * *
  49. * Nomenclature *
  50. * *
  51. * S ::= "SOUTH" ::= one pixel move in +y-direction *
  52. * N ::= "NORTH" ::= one pixel move in -y-direction *
  53. * E ::= "EAST" ::= one pixel move in +x direction *
  54. * W ::= "WEST" ::= one pixel move in -x direction *
  55. * *
  56. * The set of diagonalizable corners are described by *
  57. * the following regular expressions: *
  58. * *
  59. * DIAGONALIZABLE CORNERS *
  60. * *
  61. * S(E+|W+) a one pixel move in the +y-direction *
  62. * followed by at least one pixel in any horizontal *
  63. * direction *
  64. * *
  65. * S+W an arbitary number of pixels in the +y-direction *
  66. * followed by a single pixel move in the *
  67. * negative x-direction. *
  68. * *
  69. * EN+ a one pixel move in the positive x-direction *
  70. * followed by at least one pixel move in the negative *
  71. * x-direction *
  72. * *
  73. * (E+|W+)N at least one-pixel move in the horizontal followed *
  74. * by a single pixel move in the negative *
  75. * y-direction. *
  76. * *
  77. * Algorithm *
  78. * *
  79. * BEGIN *
  80. * <For each corner in the orginal path> *
  81. * BEGIN *
  82. * <if the corner is diagonalizable> THEN *
  83. * *
  84. * <just draw a single diagonal line> *
  85. * ELSE *
  86. * <draw both legs of the original corner> *
  87. * END *
  88. * *
  89. * <Go around the path once again, merging successive *
  90. * identical moves into single lines> *
  91. * END *
  92. * *
  93. * In the code, both of these steps are done in parallel *
  94. * *
  95. * Further Improvements *
  96. * *
  97. * The output path the I generate with this algorithm will contain only *
  98. * points that were vertices of the original axial path. A larger of *
  99. * regular expressions could be searched for if I were willing to *
  100. * consider using new vertices for the output path. For example *
  101. * the regular exprssios N+WN and S+ES describe two "chicane turns" that *
  102. * can be diagonalized. The price to be paid is the a more complex *
  103. * code path. *
  104. * *
  105. \**************************************************************************/
  106. #include "precomp.hpp"
  107. /******************************Public*Routine******************************\
  108. * RegionToPath::ConvertRegionToPath *
  109. * *
  110. * Takes an enumerable clip region as input and outputs a path *
  111. * *
  112. * Assumptions *
  113. * *
  114. * 0. *this is the original path which will not be changed. *
  115. * 1. All points on the path lie on integers *
  116. * 2. All subpaths have the inside on the left *
  117. * 3. All subpaths are closed *
  118. * *
  119. * History: *
  120. * Mon 13-Sep-1993 15:53:50 by Kirk Olynyk [kirko] *
  121. * Wrote it. *
  122. \**************************************************************************/
  123. BOOL RegionToPath::ConvertRegionToPath(const DpRegion* inRegion,
  124. DynPointArray& newPoints,
  125. DynByteArray& newTypes)
  126. {
  127. BOOL result;
  128. curIndex = 0;
  129. // initialize array to reasonable no. of points + types
  130. points = &newPoints;
  131. types = &newTypes;
  132. newPoints.Reset();
  133. newTypes.Reset();
  134. region = inRegion;
  135. if (region->IsSimple())
  136. {
  137. GpRect bounds;
  138. region->GetBounds(&bounds);
  139. newPoints.Add(GpPoint(bounds.X, bounds.Y));
  140. newPoints.Add(GpPoint(bounds.X + bounds.Width, bounds.Y));
  141. newPoints.Add(GpPoint(bounds.X + bounds.Width, bounds.Y + bounds.Height));
  142. newPoints.Add(GpPoint(bounds.X, bounds.Y + bounds.Height));
  143. newTypes.Add(PathPointTypeStart);
  144. newTypes.Add(PathPointTypeLine);
  145. newTypes.Add(PathPointTypeLine);
  146. newTypes.Add(PathPointTypeLine | PathPointTypeCloseSubpath);
  147. return TRUE;
  148. }
  149. inPoints.Reset();
  150. inTypes.Reset();
  151. // convert region to right angle piecewise line segments
  152. if (region->GetOutlinePoints(inPoints, inTypes) == TRUE)
  153. {
  154. curPoint = (GpPoint*) inPoints.GetDataBuffer();
  155. curType = (BYTE*) inTypes.GetDataBuffer();
  156. BOOL result = TRUE;
  157. lastPoint = &inPoints.Last();
  158. while (curPoint<=lastPoint && result)
  159. {
  160. endSubpath = FALSE;
  161. firstPoint = curPoint;
  162. result = DiagonalizePath();
  163. }
  164. return result;
  165. }
  166. else
  167. return FALSE;
  168. }
  169. /******************************Public*Routine******************************\
  170. * RTP_PATHMEMOBJ::bWritePoint *
  171. * *
  172. * This routine takes as input a candidate point for writing. However *
  173. * this routine is smart in that it analyzes the stream of candidate *
  174. * points looking for consecutive sub-sets of points that all lie on the *
  175. * same line. When such a case is recognized, then only the endpoints of *
  176. * the interpolating line are actually added to the output path. *
  177. * *
  178. * I do not go to a great deal of trouble to determine if a candidate *
  179. * point is on a line. All that I do is to see if the vector increment *
  180. * to the new point is the same as the increment between prior points *
  181. * in the input path. *
  182. * *
  183. * History: *
  184. * Mon 13-Sep-1993 15:53:35 by Kirk Olynyk [kirko] *
  185. * Wrote it. *
  186. \**************************************************************************/
  187. BOOL RegionToPath::WritePoint()
  188. {
  189. GpPoint NewAB;
  190. BOOL result = TRUE;
  191. int jA = curIndex;
  192. if (outPts == 2)
  193. {
  194. NewAB.X = pts[jA].X - writePts[1].X;
  195. NewAB.Y = pts[jA].Y - writePts[1].Y;
  196. if (NewAB.X != AB.X || NewAB.Y != AB.Y)
  197. {
  198. points->Add(writePts[0]);
  199. types->Add(PathPointTypeLine);
  200. writePts[0] = writePts[1];
  201. AB = NewAB;
  202. }
  203. writePts[1] = pts[jA];
  204. }
  205. else if (outPts == 0)
  206. {
  207. writePts[0] = pts[jA];
  208. outPts += 1;
  209. }
  210. else if (outPts == 1)
  211. {
  212. writePts[1] = pts[jA];
  213. AB.X = writePts[1].X - writePts[0].X;
  214. AB.Y = writePts[1].Y - writePts[0].Y;
  215. outPts += 1;
  216. }
  217. else
  218. {
  219. RIP(("RegionToPath::WritePoint -- point count is bad"));
  220. result = FALSE;
  221. }
  222. return(result);
  223. }
  224. /******************************Public*Routine******************************\
  225. * bFetchNextPoint ... in sub-path *
  226. * *
  227. * History: *
  228. * Tue 14-Sep-1993 14:13:01 by Kirk Olynyk [kirko] *
  229. * Wrote it. *
  230. \**************************************************************************/
  231. BOOL RegionToPath::FetchNextPoint()
  232. {
  233. INT oldIndex = curIndex;
  234. curIndex = (curIndex + 1) % 3;
  235. // only output the first point if at end of a subpath
  236. if (endSubpath)
  237. {
  238. // end of subpath, add first point on end of new path
  239. flags[oldIndex] = 0;
  240. pts[oldIndex] = *firstPoint;
  241. return TRUE;
  242. }
  243. pts[oldIndex] = *curPoint;
  244. // check for end subpath only?
  245. if (*curType & PathPointTypeCloseSubpath)
  246. {
  247. endSubpath = TRUE;
  248. flags[oldIndex] = LastPointFlag;
  249. }
  250. else
  251. {
  252. flags[oldIndex] = 0;
  253. }
  254. curPoint++;
  255. curType++;
  256. return TRUE;
  257. }
  258. /******************************Public*Routine******************************\
  259. * Path2Region::bDiagonalizeSubPathRTP_PATHMEMOBJ::bDiagonalizeSubPath *
  260. * *
  261. * History: *
  262. * Tue 14-Sep-1993 12:47:49 by Kirk Olynyk [kirko] *
  263. * Wrote it. *
  264. \**************************************************************************/
  265. inline VOID RotateBackward(INT& x, INT& y, INT& z)
  266. {
  267. INT temp;
  268. temp = x;
  269. x = z;
  270. z = y;
  271. y = temp;
  272. }
  273. inline VOID RotateForward(INT& x, INT& y, INT& z)
  274. {
  275. INT temp;
  276. temp = x;
  277. x = y;
  278. y = z;
  279. z = temp;
  280. }
  281. BOOL RegionToPath::DiagonalizePath()
  282. {
  283. INT AB; // Length of leg A->B
  284. INT BC; // Length of second leg B->C
  285. INT bH; // set to 1 if second leg is horizontal
  286. INT jA,jB,jC;
  287. register BOOL bRet = TRUE; // if FALSE then return immediately
  288. // otherwise keep processing.
  289. outPts = 0; // no points so far in the write buffer
  290. curIndex = 0; // set the start of the circular buffer
  291. lastCount = 0;
  292. // Fill the circular buffer with the first three points of the
  293. // path. The three member buffer, defines two successive lines, or
  294. // one corner (the path is guaranteed to be composed of alternating
  295. // lines along the x-axis and y-axis). I shall label the three vertices
  296. // of the corner A,B, and C. The point A always resides at ax[j],
  297. // point B resides at ax[iMod3[j+1]], and point C resides at
  298. // ax[iMod3[j+2]] where j can have one of the values 0, 1, 2.
  299. if (bRet = FetchNextPoint() && FetchNextPoint() && FetchNextPoint())
  300. {
  301. ASSERTMSG(curIndex == 0, ("RegionToPath::DiagonalizeSubPath()"
  302. " -- curIndex != 0"));
  303. // bH ::= <is the second leg of the corner horizontal?>
  304. //
  305. // if the second leg of the corner is horizontal set bH=1 otherwise
  306. // set bH=0. Calculate the length of the first leg of the corner
  307. // and save it in fxAB. Note that I do not need to use the iMod3
  308. // modulus operation since j==0.
  309. if (pts[2].Y == pts[1].Y)
  310. {
  311. bH = 1;
  312. AB = pts[1].Y - pts[0].Y;
  313. }
  314. else
  315. {
  316. bH = 0;
  317. AB = pts[1].X - pts[0].X;
  318. }
  319. // Start a new subpath at the first point of the subpath.
  320. points->Add(pts[0]);
  321. types->Add(PathPointTypeStart);
  322. jA = 0;
  323. jB = 1;
  324. jC = 2;
  325. }
  326. while (bRet)
  327. {
  328. if (!(flags[jA] & LastPointFlag))
  329. {
  330. // Assert that the the legs of the corner are along
  331. // the axes, and that the two legs are mutually
  332. // orthogonal
  333. ASSERTMSG(pts[jC].X == pts[jB].X || pts[jC].Y == pts[jB].Y,
  334. ("Bad Path :: C-B is not axial"));
  335. ASSERTMSG(pts[jA].X == pts[jB].X || pts[jA].Y == pts[jB].Y,
  336. ("Bad Path :: B-A is not axial"));
  337. ASSERTMSG(
  338. (pts[jC].X - pts[jB].X) *
  339. (pts[jB].X - pts[jA].X)
  340. +
  341. (pts[jC].Y - pts[jB].Y) *
  342. (pts[jB].Y - pts[jA].Y)
  343. == 0,
  344. ("Bad Path :: B-A is not orthogonal to C-B")
  345. );
  346. }
  347. // If the first vertex of the corner is the last point in the
  348. // original subpath then we terminate the processing. This point
  349. // has either been recorded with PATHMEMOBJ::bMoveTo or
  350. // PATHMEMOBJ::bPolyLineTo. All that remains is to close the
  351. // subpath which is done outside the while loop
  352. if (flags[jA] & LastPointFlag)
  353. break;
  354. // There are two paths through the following if-else clause
  355. // They are for VERTICAL-HORIZONTAL and HORIZONTAL-VERTICAL
  356. // corners respectively. These two clauses are identical
  357. // except for the interchange of ".x" with ".y". It might be
  358. // a good idea to have macros or subrouines for these sections
  359. // in order that they be guranteed to be identical.
  360. // Is the second leg of the corner horizontal?
  361. if (bH)
  362. {
  363. // Yes, the second leg of the corner is horizontal
  364. BC = pts[jC].X - pts[jB].X;
  365. // Is the corner diagonalizable?
  366. if ((AB > 0) && ((AB == 1) || (BC == -1)))
  367. {
  368. // Yes, the corner is diagonalizable
  369. //
  370. // If the middle of the corner was the last point in the
  371. // original path then the last point in the output path
  372. // is the first point in the corner. This is because the
  373. // last line in the output path is this diagonalized
  374. // corner which will be produced automatically by the
  375. // CloseFigure() call after this while-loop. Thus, in
  376. // this case we would just break out of the loop.
  377. if (flags[jB] & LastPointFlag)
  378. break;
  379. // The corner is diagonalizable. This means that we are no
  380. // longer interested in the first two points of this corner.
  381. // We therefore fetch the next two points of the path
  382. // an place them in our circular corner-buffer.
  383. if (!(bRet = FetchNextPoint() && FetchNextPoint()))
  384. break;
  385. // under modulo 3 arithmetic, incrementing by 2 is
  386. // equivalent to decrementing by 1
  387. RotateBackward(jA,jB,jC);
  388. // fxAB is set to the length of the first leg of the new
  389. // corner.
  390. AB = pts[jB].Y - pts[jA].Y;
  391. }
  392. else
  393. {
  394. // No, the corner is not diagonalizable
  395. //
  396. // The corner cannot be diagonalized. Advance the corner
  397. // to the next point in the original path. The orientation
  398. // of the second leg of the corner will change. The length
  399. // of the first leg of the new corner is set equal to the
  400. // length of the second leg of the previous corner.
  401. if (!(bRet = FetchNextPoint()))
  402. break;
  403. RotateForward(jA,jB,jC);
  404. bH ^= 1;
  405. AB = BC;
  406. }
  407. }
  408. else
  409. {
  410. // Diagonalize the HORIZONTAL->VERTICAL corner
  411. BC = pts[jC].Y - pts[jB].Y;
  412. if ((BC < 0) && ((AB == 1) || (BC == -1)))
  413. {
  414. if (flags[jB] & LastPointFlag)
  415. break;
  416. if (!(bRet = FetchNextPoint() && FetchNextPoint()))
  417. break;
  418. RotateBackward(jA,jB,jC);
  419. AB = pts[jB].X - pts[jA].X;
  420. }
  421. else
  422. {
  423. if (!(bRet = FetchNextPoint()))
  424. break;
  425. RotateForward(jA,jB,jC);
  426. bH ^= 1;
  427. AB = BC;
  428. }
  429. }
  430. if (!(bRet = WritePoint()))
  431. break;
  432. }
  433. if (bRet)
  434. {
  435. ASSERTMSG(outPts == 2, ("GDI Region To Path -- numPts is not 2"));
  436. points->Add(writePts[0]);
  437. points->Add(writePts[1]);
  438. types->Add(PathPointTypeLine);
  439. types->Add(PathPointTypeLine | PathPointTypeCloseSubpath);
  440. }
  441. return(bRet);
  442. }