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.

638 lines
18 KiB

5 years ago
  1. /* --COPYRIGHT--,BSD
  2. * Copyright (c) 2013, Texas Instruments Incorporated
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. *
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * * Neither the name of Texas Instruments Incorporated nor the names of
  17. * its contributors may be used to endorse or promote products derived
  18. * from this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  22. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  27. * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  28. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  29. * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  30. * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. * --/COPYRIGHT--*/
  32. #include "grlib.h"
  33. //*****************************************************************************
  34. //
  35. //! \addtogroup line_api
  36. //! @{
  37. //
  38. //*****************************************************************************
  39. //*****************************************************************************
  40. //
  41. //! Draws a horizontal line.
  42. //!
  43. //! \param pContext is a pointer to the drawing context to use.
  44. //! \param lX1 is the X coordinate of one end of the line.
  45. //! \param lX2 is the X coordinate of the other end of the line.
  46. //! \param lY is the Y coordinate of the line.
  47. //!
  48. //! This function draws a horizontal line, taking advantage of the fact that
  49. //! the line is horizontal to draw it more efficiently. The clipping of the
  50. //! horizontal line to the clipping rectangle is performed within this routine;
  51. //! the display driver's horizontal line routine is used to perform the actual
  52. //! line drawing.
  53. //!
  54. //! \return None.
  55. //
  56. //*****************************************************************************
  57. void
  58. GrLineDrawH(const tContext *pContext, long lX1, long lX2, long lY)
  59. {
  60. long lTemp;
  61. //
  62. // Check the arguments.
  63. //
  64. assert(pContext);
  65. //
  66. // If the Y coordinate of this line is not in the clipping region, then
  67. // there is nothing to be done.
  68. //
  69. if((lY < pContext->sClipRegion.sYMin) ||
  70. (lY > pContext->sClipRegion.sYMax))
  71. {
  72. return;
  73. }
  74. //
  75. // Swap the X coordinates if the first is larger than the second.
  76. //
  77. if(lX1 > lX2)
  78. {
  79. lTemp = lX1;
  80. lX1 = lX2;
  81. lX2 = lTemp;
  82. }
  83. //
  84. // If the entire line is outside the clipping region, then there is nothing
  85. // to be done.
  86. //
  87. if((lX1 > pContext->sClipRegion.sXMax) ||
  88. (lX2 < pContext->sClipRegion.sXMin))
  89. {
  90. return;
  91. }
  92. //
  93. // Clip the starting coordinate to the left side of the clipping region if
  94. // required.
  95. //
  96. if(lX1 < pContext->sClipRegion.sXMin)
  97. {
  98. lX1 = pContext->sClipRegion.sXMin;
  99. }
  100. //
  101. // Clip the ending coordinate to the right side of the clipping region if
  102. // required.
  103. //
  104. if(lX2 > pContext->sClipRegion.sXMax)
  105. {
  106. lX2 = pContext->sClipRegion.sXMax;
  107. }
  108. //
  109. // Call the low level horizontal line drawing routine.
  110. //
  111. DpyLineDrawH(pContext->pDisplay, lX1, lX2, lY, pContext->ulForeground);
  112. }
  113. //*****************************************************************************
  114. //
  115. //! Draws a vertical line.
  116. //!
  117. //! \param pContext is a pointer to the drawing context to use.
  118. //! \param lX is the X coordinate of the line.
  119. //! \param lY1 is the Y coordinate of one end of the line.
  120. //! \param lY2 is the Y coordinate of the other end of the line.
  121. //!
  122. //! This function draws a vertical line, taking advantage of the fact that the
  123. //! line is vertical to draw it more efficiently. The clipping of the vertical
  124. //! line to the clipping rectangle is performed within this routine; the
  125. //! display driver's vertical line routine is used to perform the actual line
  126. //! drawing.
  127. //!
  128. //! \return None.
  129. //
  130. //*****************************************************************************
  131. void
  132. GrLineDrawV(const tContext *pContext, long lX, long lY1, long lY2)
  133. {
  134. long lTemp;
  135. //
  136. // Check the arguments.
  137. //
  138. assert(pContext);
  139. //
  140. // If the X coordinate of this line is not within the clipping region, then
  141. // there is nothing to be done.
  142. //
  143. if((lX < pContext->sClipRegion.sXMin) ||
  144. (lX > pContext->sClipRegion.sXMax))
  145. {
  146. return;
  147. }
  148. //
  149. // Swap the Y coordinates if the first is larger than the second.
  150. //
  151. if(lY1 > lY2)
  152. {
  153. lTemp = lY1;
  154. lY1 = lY2;
  155. lY2 = lTemp;
  156. }
  157. //
  158. // If the entire line is out of the clipping region, then there is nothing
  159. // to be done.
  160. //
  161. if((lY1 > pContext->sClipRegion.sYMax) ||
  162. (lY2 < pContext->sClipRegion.sYMin))
  163. {
  164. return;
  165. }
  166. //
  167. // Clip the starting coordinate to the top side of the clipping region if
  168. // required.
  169. //
  170. if(lY1 < pContext->sClipRegion.sYMin)
  171. {
  172. lY1 = pContext->sClipRegion.sYMin;
  173. }
  174. //
  175. // Clip the ending coordinate to the bottom side of the clipping region if
  176. // required.
  177. //
  178. if(lY2 > pContext->sClipRegion.sYMax)
  179. {
  180. lY2 = pContext->sClipRegion.sYMax;
  181. }
  182. //
  183. // Call the low level vertical line drawing routine.
  184. //
  185. DpyLineDrawV(pContext->pDisplay, lX, lY1, lY2, pContext->ulForeground);
  186. }
  187. //*****************************************************************************
  188. //
  189. //! Computes the clipping code used by the Cohen-Sutherland clipping algorithm.
  190. //!
  191. //! \param pContext is a pointer to the drawing context to use.
  192. //! \param lX is the X coordinate of the point.
  193. //! \param lY is the Y coordinate of the point.
  194. //!
  195. //! This function computes the clipping code used by the Cohen-Sutherland
  196. //! clipping algorithm. Clipping is performed by classifying the endpoints of
  197. //! the line based on their relation to the clipping region; this determines
  198. //! those relationships.
  199. //!
  200. //! \return Returns the clipping code.
  201. //
  202. //*****************************************************************************
  203. static long
  204. GrClipCodeGet(const tContext *pContext, long lX, long lY)
  205. {
  206. long lCode;
  207. //
  208. // Initialize the clipping code to zero.
  209. //
  210. lCode = 0;
  211. //
  212. // Set bit zero of the clipping code if the Y coordinate is above the
  213. // clipping region.
  214. //
  215. if(lY < pContext->sClipRegion.sYMin)
  216. {
  217. lCode |= 1;
  218. }
  219. //
  220. // Set bit one of the clipping code if the Y coordinate is below the
  221. // clipping region.
  222. //
  223. if(lY > pContext->sClipRegion.sYMax)
  224. {
  225. lCode |= 2;
  226. }
  227. //
  228. // Set bit two of the clipping code if the X coordinate is to the left of
  229. // the clipping region.
  230. //
  231. if(lX < pContext->sClipRegion.sXMin)
  232. {
  233. lCode |= 4;
  234. }
  235. //
  236. // Set bit three of the clipping code if the X coordinate is to the right
  237. // of the clipping region.
  238. //
  239. if(lX > pContext->sClipRegion.sXMax)
  240. {
  241. lCode |= 8;
  242. }
  243. //
  244. // Return the clipping code.
  245. //
  246. return(lCode);
  247. }
  248. //*****************************************************************************
  249. //
  250. //! Clips a line to the clipping region.
  251. //!
  252. //! \param pContext is a pointer to the drawing context to use.
  253. //! \param plX1 is the X coordinate of the start of the line.
  254. //! \param plY1 is the Y coordinate of the start of the line.
  255. //! \param plX2 is the X coordinate of the end of the line.
  256. //! \param plY2 is the Y coordinate of the end of the line.
  257. //!
  258. //! This function clips a line to the extents of the clipping region using the
  259. //! Cohen-Sutherland clipping algorithm. The ends of the line are classified
  260. //! based on their relation to the clipping region, and the codes are used to
  261. //! either trivially accept a line (both end points within the clipping
  262. //! region), trivially reject a line (both end points to one side of the
  263. //! clipping region), or to adjust an endpoint one axis at a time to the edge
  264. //! of the clipping region until the line can either be trivially accepted or
  265. //! trivially rejected.
  266. //!
  267. //! The provided coordinates are modified such that they reside within the
  268. //! extents of the clipping region if the line is not rejected. If it is
  269. //! rejected, the coordinates may be modified during the process of attempting
  270. //! to clip them.
  271. //!
  272. //! \return Returns one if the clipped line lies within the extent of the
  273. //! clipping region and zero if it does not.
  274. //
  275. //*****************************************************************************
  276. static long
  277. GrLineClip(const tContext *pContext, long *plX1, long *plY1, long *plX2,
  278. long *plY2)
  279. {
  280. long lCode, lCode1, lCode2, lX, lY;
  281. //
  282. // Compute the clipping codes for the two endpoints of the line.
  283. //
  284. lCode1 = GrClipCodeGet(pContext, *plX1, *plY1);
  285. lCode2 = GrClipCodeGet(pContext, *plX2, *plY2);
  286. //
  287. // Loop forever. This loop will be explicitly broken out of when the line
  288. // is either trivially accepted or trivially rejected.
  289. //
  290. while(1)
  291. {
  292. //
  293. // If both codes are zero, then both points lie within the extent of
  294. // the clipping region. In this case, trivally accept the line.
  295. //
  296. if((lCode1 == 0) && (lCode2 == 0))
  297. {
  298. return(1);
  299. }
  300. //
  301. // If the intersection of the codes is non-zero, then the line lies
  302. // entirely off one edge of the clipping region. In this case,
  303. // trivally reject the line.
  304. //
  305. if((lCode1 & lCode2) != 0)
  306. {
  307. return(0);
  308. }
  309. //
  310. // Determine the end of the line to move. The first end of the line is
  311. // moved until it is within the clipping region, and then the second
  312. // end of the line is moved until it is also within the clipping
  313. // region.
  314. //
  315. if(lCode1)
  316. {
  317. lCode = lCode1;
  318. }
  319. else
  320. {
  321. lCode = lCode2;
  322. }
  323. //
  324. // See if this end of the line lies above the clipping region.
  325. //
  326. if(lCode & 1)
  327. {
  328. //
  329. // Move this end of the line to the intersection of the line and
  330. // the top of the clipping region.
  331. //
  332. lX = (*plX1 + (((*plX2 - *plX1) *
  333. (pContext->sClipRegion.sYMin - *plY1)) /
  334. (*plY2 - *plY1)));
  335. lY = pContext->sClipRegion.sYMin;
  336. }
  337. //
  338. // Otherwise, see if this end of the line lies below the clipping
  339. // region.
  340. //
  341. else if(lCode & 2)
  342. {
  343. //
  344. // Move this end of the line to the intersection of the line and
  345. // the bottom of the clipping region.
  346. //
  347. lX = (*plX1 + (((*plX2 - *plX1) *
  348. (pContext->sClipRegion.sYMax - *plY1)) /
  349. (*plY2 - *plY1)));
  350. lY = pContext->sClipRegion.sYMax;
  351. }
  352. //
  353. // Otherwise, see if this end of the line lies to the left of the
  354. // clipping region.
  355. //
  356. else if(lCode & 4)
  357. {
  358. //
  359. // Move this end of the line to the intersection of the line and
  360. // the left side of the clipping region.
  361. //
  362. lX = pContext->sClipRegion.sXMin;
  363. lY = (*plY1 + (((*plY2 - *plY1) *
  364. (pContext->sClipRegion.sXMin - *plX1)) /
  365. (*plX2 - *plX1)));
  366. }
  367. //
  368. // Otherwise, this end of the line lies to the right of the clipping
  369. // region.
  370. //
  371. else
  372. {
  373. //
  374. // Move this end of the line to the intersection of the line and
  375. // the right side of the clipping region.
  376. //
  377. lX = pContext->sClipRegion.sXMax;
  378. lY = (*plY1 + (((*plY2 - *plY1) *
  379. (pContext->sClipRegion.sXMax - *plX1)) /
  380. (*plX2 - *plX1)));
  381. }
  382. //
  383. // See which end of the line just moved.
  384. //
  385. if(lCode1)
  386. {
  387. //
  388. // Save the new coordinates for the start of the line.
  389. //
  390. *plX1 = lX;
  391. *plY1 = lY;
  392. //
  393. // Recompute the clipping code for the start of the line.
  394. //
  395. lCode1 = GrClipCodeGet(pContext, lX, lY);
  396. }
  397. else
  398. {
  399. //
  400. // Save the new coordinates for the end of the line.
  401. //
  402. *plX2 = lX;
  403. *plY2 = lY;
  404. //
  405. // Recompute the clipping code for the end of the line.
  406. //
  407. lCode2 = GrClipCodeGet(pContext, lX, lY);
  408. }
  409. }
  410. }
  411. //*****************************************************************************
  412. //
  413. //! Draws a line.
  414. //!
  415. //! \param pContext is a pointer to the drawing context to use.
  416. //! \param lX1 is the X coordinate of the start of the line.
  417. //! \param lY1 is the Y coordinate of the start of the line.
  418. //! \param lX2 is the X coordinate of the end of the line.
  419. //! \param lY2 is the Y coordinate of the end of the line.
  420. //!
  421. //! This function draws a line, utilizing GrLineDrawH() and GrLineDrawV() to
  422. //! draw the line as efficiently as possible. The line is clipped to the
  423. //! clippping rectangle using the Cohen-Sutherland clipping algorithm, and then
  424. //! scan converted using Bresenham's line drawing algorithm.
  425. //!
  426. //! \return None.
  427. //
  428. //*****************************************************************************
  429. void
  430. GrLineDraw(const tContext *pContext, long lX1, long lY1, long lX2, long lY2)
  431. {
  432. long lError, lDeltaX, lDeltaY, lYStep, bSteep;
  433. //
  434. // Check the arguments.
  435. //
  436. assert(pContext);
  437. //
  438. // See if this is a vertical line.
  439. //
  440. if(lX1 == lX2)
  441. {
  442. //
  443. // It is more efficient to avoid Bresenham's algorithm when drawing a
  444. // vertical line, so use the vertical line routine to draw this line.
  445. //
  446. GrLineDrawV(pContext, lX1, lY1, lY2);
  447. //
  448. // The line has ben drawn, so return.
  449. //
  450. return;
  451. }
  452. //
  453. // See if this is a horizontal line.
  454. //
  455. if(lY1 == lY2)
  456. {
  457. //
  458. // It is more efficient to avoid Bresenham's algorithm when drawing a
  459. // horizontal line, so use the horizontal line routien to draw this
  460. // line.
  461. //
  462. GrLineDrawH(pContext, lX1, lX2, lY1);
  463. //
  464. // The line has ben drawn, so return.
  465. //
  466. return;
  467. }
  468. //
  469. // Clip this line if necessary, and return without drawing anything if the
  470. // line does not cross the clipping region.
  471. //
  472. if(GrLineClip(pContext, &lX1, &lY1, &lX2, &lY2) == 0)
  473. {
  474. return;
  475. }
  476. //
  477. // Determine if the line is steep. A steep line has more motion in the Y
  478. // direction than the X direction.
  479. //
  480. if(((lY2 > lY1) ? (lY2 - lY1) : (lY1 - lY2)) >
  481. ((lX2 > lX1) ? (lX2 - lX1) : (lX1 - lX2)))
  482. {
  483. bSteep = 1;
  484. }
  485. else
  486. {
  487. bSteep = 0;
  488. }
  489. //
  490. // If the line is steep, then swap the X and Y coordinates.
  491. //
  492. if(bSteep)
  493. {
  494. lError = lX1;
  495. lX1 = lY1;
  496. lY1 = lError;
  497. lError = lX2;
  498. lX2 = lY2;
  499. lY2 = lError;
  500. }
  501. //
  502. // If the starting X coordinate is larger than the ending X coordinate,
  503. // then swap the start and end coordinates.
  504. //
  505. if(lX1 > lX2)
  506. {
  507. lError = lX1;
  508. lX1 = lX2;
  509. lX2 = lError;
  510. lError = lY1;
  511. lY1 = lY2;
  512. lY2 = lError;
  513. }
  514. //
  515. // Compute the difference between the start and end coordinates in each
  516. // axis.
  517. //
  518. lDeltaX = lX2 - lX1;
  519. lDeltaY = (lY2 > lY1) ? (lY2 - lY1) : (lY1 - lY2);
  520. //
  521. // Initialize the error term to negative half the X delta.
  522. //
  523. lError = -lDeltaX / 2;
  524. //
  525. // Determine the direction to step in the Y axis when required.
  526. //
  527. if(lY1 < lY2)
  528. {
  529. lYStep = 1;
  530. }
  531. else
  532. {
  533. lYStep = -1;
  534. }
  535. //
  536. // Loop through all the points along the X axis of the line.
  537. //
  538. for(; lX1 <= lX2; lX1++)
  539. {
  540. //
  541. // See if this is a steep line.
  542. //
  543. if(bSteep)
  544. {
  545. //
  546. // Plot this point of the line, swapping the X and Y coordinates.
  547. //
  548. DpyPixelDraw(pContext->pDisplay, lY1, lX1, pContext->ulForeground);
  549. }
  550. else
  551. {
  552. //
  553. // Plot this point of the line, using the coordinates as is.
  554. //
  555. DpyPixelDraw(pContext->pDisplay, lX1, lY1, pContext->ulForeground);
  556. }
  557. //
  558. // Increment the error term by the Y delta.
  559. //
  560. lError += lDeltaY;
  561. //
  562. // See if the error term is now greater than zero.
  563. //
  564. if(lError > 0)
  565. {
  566. //
  567. // Take a step in the Y axis.
  568. //
  569. lY1 += lYStep;
  570. //
  571. // Decrement the error term by the X delta.
  572. //
  573. lError -= lDeltaX;
  574. }
  575. }
  576. }
  577. //*****************************************************************************
  578. //
  579. // Close the Doxygen group.
  580. //! @}
  581. //
  582. //*****************************************************************************