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.

531 lines
23 KiB

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