nxglib_splitline.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. /****************************************************************************
  2. * graphics/nxglib/nxglib_splitline.c
  3. *
  4. * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include <nuttx/config.h>
  39. #include <string.h>
  40. #include <errno.h>
  41. #include <stdlib.h>
  42. #include <debug.h>
  43. #include <nuttx/nx/nxglib.h>
  44. /****************************************************************************
  45. * Pre-Processor Definitions
  46. ****************************************************************************/
  47. /****************************************************************************
  48. * Private Types
  49. ****************************************************************************/
  50. struct b16point_s
  51. {
  52. b16_t x;
  53. b16_t y;
  54. };
  55. /****************************************************************************
  56. * Private Data
  57. ****************************************************************************/
  58. /****************************************************************************
  59. * Public Data
  60. ****************************************************************************/
  61. /****************************************************************************
  62. * Private Functions
  63. ****************************************************************************/
  64. static b16_t nxgl_interpolate(b16_t x, b16_t dy, b16_t dxdy)
  65. {
  66. b16_t dx = b16mulb16(dy, dxdy);
  67. return x + dx;
  68. }
  69. /****************************************************************************
  70. * Public Functions
  71. ****************************************************************************/
  72. /****************************************************************************
  73. * Name: nxgl_splitline
  74. *
  75. * Description:
  76. * In the general case, a line with width can be represented as a
  77. * parallelogram with a triangle at the top and bottom. Triangles and
  78. * parallelograms are both degenerate versions of a trapeziod. This
  79. * function breaks a wide line into triangles and trapezoids. This
  80. * function also detects other degenerate cases:
  81. *
  82. * 1. If y1 == y2 then the line is horizontal and is better represented
  83. * as a rectangle.
  84. * 2. If x1 == x2 then the line is vertical and also better represented
  85. * as a rectangle.
  86. * 3. If the width of the line is 1, then there are no triangles at the
  87. * top and bottome (this may also be the case if the width is narrow
  88. * and the line is near vertical).
  89. * 4. If the line is oriented is certain angles, it may consist only of
  90. * the upper and lower triangles with no trapezoid in between. In
  91. * this case, 3 trapezoids will be returned, but traps[1] will be
  92. * degenerate.
  93. *
  94. * Input parameters:
  95. * vector - A pointer to the vector described the line to be drawn.
  96. * traps - A pointer to a array of trapezoids (size 3).
  97. * rect - A pointer to a rectangle.
  98. *
  99. * Returned value:
  100. * 0: Line successfully broken up into three trapezoids. Values in
  101. * traps[0], traps[1], and traps[2] are valid.
  102. * 1: Line successfully represented by one trapezoid. Value in traps[1]
  103. * is valid.
  104. * 2: Line successfully represented by one rectangle. Value in rect is
  105. * valid
  106. * <0: On errors, a negated errno value is returned.
  107. *
  108. ****************************************************************************/
  109. int nxgl_splitline(FAR struct nxgl_vector_s *vector,
  110. FAR struct nxgl_trapezoid_s *traps,
  111. FAR struct nxgl_rect_s *rect,
  112. nxgl_coord_t linewidth)
  113. {
  114. struct nxgl_vector_s line;
  115. nxgl_coord_t iheight;
  116. nxgl_coord_t iwidth;
  117. nxgl_coord_t iyoffset;
  118. struct b16point_s quad[4];
  119. b16_t b16xoffset;
  120. b16_t b16yoffset;
  121. b16_t b16dxdy;
  122. b16_t angle;
  123. b16_t cosangle;
  124. b16_t sinangle;
  125. b16_t b16x;
  126. b16_t b16y;
  127. gvdbg("vector: (%d,%d)->(%d,%d) linewidth: %d\n",
  128. vector->pt1.x, vector->pt1.y, vector->pt2.x, vector->pt2.y, linewidth);
  129. /* First, check the linewidth */
  130. if (linewidth < 1)
  131. {
  132. return -EINVAL;
  133. }
  134. /* Then make sure that the start position of the line is above the end
  135. * position of the line... in raster order.
  136. */
  137. if (vector->pt1.y < vector->pt2.y)
  138. {
  139. /* Vector is already in raster order */
  140. memcpy(&line, vector, sizeof(struct nxgl_vector_s));
  141. }
  142. else if (vector->pt1.y > vector->pt2.y)
  143. {
  144. /* Swap the top and bottom */
  145. line.pt1.x = vector->pt2.x;
  146. line.pt1.y = vector->pt2.y;
  147. line.pt2.x = vector->pt1.x;
  148. line.pt2.y = vector->pt1.y;
  149. }
  150. else /* if (vector->pt1.y == vector->pt2.y) */
  151. {
  152. /* First degenerate case: The line is horizontal. */
  153. if (vector->pt1.x < vector->pt2.x)
  154. {
  155. rect->pt1.x = vector->pt1.x;
  156. rect->pt2.x = vector->pt2.x;
  157. }
  158. else
  159. {
  160. rect->pt1.x = vector->pt2.x;
  161. rect->pt2.x = vector->pt1.x;
  162. }
  163. /* The height of the rectangle is the width of the line, half above
  164. * and half below.
  165. */
  166. rect->pt1.y = vector->pt1.y - (linewidth >> 1);
  167. rect->pt2.y = rect->pt1.y + linewidth - 1;
  168. gvdbg("Horizontal rect: (%d,%d),(%d,%d)\n",
  169. rect->pt1.x, rect->pt1.y, rect->pt2.x, rect->pt2.y);
  170. return 2;
  171. }
  172. /* Check if the line is vertical */
  173. if (line.pt1.x == line.pt2.x)
  174. {
  175. /* Second degenerate case: The line is vertical. */
  176. rect->pt1.y = line.pt1.y;
  177. rect->pt2.y = line.pt2.y;
  178. rect->pt1.x = line.pt1.x - (linewidth >> 1);
  179. rect->pt2.x = rect->pt1.x + linewidth - 1;
  180. gvdbg("Vertical rect: (%d,%d),(%d,%d)\n",
  181. rect->pt1.x, rect->pt1.y, rect->pt2.x, rect->pt2.y);
  182. return 2;
  183. }
  184. /* The final degenerate case */
  185. if (linewidth == 1 &&
  186. abs(line.pt2.x - line.pt1.x) < (line.pt2.y - line.pt1.y))
  187. {
  188. /* A close to vertical line of width 1 is basically
  189. * a single parallelogram of width 1.
  190. */
  191. traps[1].top.x1 = itob16(line.pt1.x);
  192. traps[1].top.x2 = traps[1].top.x1;
  193. traps[1].top.y = line.pt1.y;
  194. traps[1].bot.x1 = itob16(line.pt2.x);
  195. traps[1].bot.x2 = traps[1].bot.x1;
  196. traps[1].bot.y = line.pt2.y;
  197. gvdbg("Vertical traps[1]: (%08x,%08x,%d),(%08x,%08x, %d)\n",
  198. traps[1].top.x1, traps[1].top.x2, traps[1].top.y,
  199. traps[1].bot.x1, traps[1].bot.x2, traps[1].bot.y);
  200. return 1;
  201. }
  202. /* Okay, then what remains is interesting.
  203. *
  204. * iheight = |y2 - y1|
  205. * iwidth = |x2 - x1|
  206. */
  207. iheight = line.pt2.y - line.pt1.y + 1;
  208. if (line.pt1.x < line.pt2.x)
  209. {
  210. iwidth = line.pt2.x - line.pt1.x + 1;
  211. }
  212. else
  213. {
  214. iwidth = line.pt1.x - line.pt2.x + 1;
  215. }
  216. /* Applying the line width to the line results in a rotated, rectangle.
  217. * Get the Y offset from an end of the original thin line to a corner of the fat line.
  218. *
  219. * Angle of line: angle = atan2(iheight, iwidth)
  220. * Y offset from line: b16yoffset = linewidth * cos(angle)
  221. *
  222. * For near verical lines, b16yoffset is be nearly zero. For near horizontal
  223. * lines, b16yOffset is be about the same as linewidth.
  224. */
  225. angle = b16atan2(itob16(iheight), itob16(iwidth));
  226. cosangle = b16cos(angle);
  227. b16yoffset = (linewidth * cosangle + 1) >> 1;
  228. /* Get the X offset from an end of the original thin line to a corner of the fat line.
  229. *
  230. * For near vertical lines, b16xoffset is about the same as linewidth. For near
  231. * horizontal lines, b16xoffset is nearly zero.
  232. */
  233. sinangle = b16sin(angle);
  234. b16xoffset = (linewidth * sinangle + 1) >> 1;
  235. gvdbg("height: %d width: %d angle: %08x b16yoffset: %08x b16xoffset: %08x\n",
  236. iheight, iwidth, angle, b16yoffset, b16xoffset);
  237. /* Now we know all four points of the rotated rectangle */
  238. iyoffset = b16toi(b16yoffset + b16HALF);
  239. if (iyoffset > 0)
  240. {
  241. /* Get the Y positions of each point */
  242. b16y = itob16(line.pt1.y);
  243. quad[0].y = b16y - b16yoffset;
  244. quad[1].y = b16y + b16yoffset;
  245. b16y = itob16(line.pt2.y);
  246. quad[2].y = b16y - b16yoffset;
  247. quad[3].y = b16y + b16yoffset;
  248. if (line.pt1.x < line.pt2.x)
  249. {
  250. /* Line is going "south east". Get the X positions of each point */
  251. b16x = itob16(line.pt1.x);
  252. quad[0].x = b16x + b16xoffset;
  253. quad[1].x = b16x - b16xoffset;
  254. b16x = itob16(line.pt2.x);
  255. quad[2].x = b16x + b16xoffset;
  256. quad[3].x = b16x - b16xoffset;
  257. gvdbg("Southeast: quad (%08x,%08x),(%08x,%08x),(%08x,%08x),(%08x,%08x)\n",
  258. quad[0].x, quad[0].y, quad[1].x, quad[1].y,
  259. quad[2].x, quad[2].y, quad[3].x, quad[3].y);
  260. /* Now we can form the trapezoids. The top of the first trapezoid
  261. * (triangle) is at quad[0]
  262. */
  263. traps[0].top.x1 = quad[0].x;
  264. traps[0].top.x2 = quad[0].x;
  265. traps[0].top.y = b16toi(quad[0].y + b16HALF);
  266. /* The bottom of the first trapezoid (triangle) may be either at
  267. * quad[1] or quad[2], depending upon orientation.
  268. */
  269. if (quad[1]. y < quad[2].y)
  270. {
  271. /* quad[1] is at the bottom left of the triangle. Interpolate
  272. * to get the corresponding point on the right side.
  273. *
  274. * Interpolation is from quad[0] along the line quad[0]->quad[2]
  275. * which as the same slope as the line (positive)
  276. */
  277. b16dxdy = itob16(iwidth) / iheight;
  278. traps[0].bot.x1 = quad[1].x;
  279. traps[0].bot.x2 = nxgl_interpolate(quad[0].x, quad[1].y - quad[0].y, b16dxdy);
  280. traps[0].bot.y = b16toi(quad[1].y + b16HALF);
  281. /* quad[1] is at the top left of the second trapezoid. quad[2} is
  282. * at the bottom right of the second trapezoid. Interpolate to get
  283. * corresponding point on the left side.
  284. *
  285. * Interpolation is from quad[1] along the line quad[1]->quad[3]
  286. * which as the same slope as the line (positive)
  287. */
  288. traps[1].top.x1 = traps[0].bot.x1;
  289. traps[1].top.x2 = traps[0].bot.x2;
  290. traps[1].top.y = traps[0].bot.y;
  291. traps[1].bot.x1 = nxgl_interpolate(traps[1].top.x1, quad[2].y - quad[1].y, b16dxdy);
  292. traps[1].bot.x2 = quad[2].x;
  293. traps[1].bot.y = b16toi(quad[2].y + b16HALF);
  294. }
  295. else
  296. {
  297. /* quad[2] is at the bottom right of the triangle. Interpolate
  298. * to get the corresponding point on the left side.
  299. *
  300. * Interpolation is from quad[0] along the line quad[0]->quad[1]
  301. * which orthogonal to the slope of the line (and negative)
  302. */
  303. b16dxdy = -itob16(iheight) / iwidth;
  304. traps[0].bot.x1 = nxgl_interpolate(quad[0].x, quad[2].y - quad[0].y, b16dxdy);
  305. traps[0].bot.x2 = quad[2].x;
  306. traps[0].bot.y = b16toi(quad[2].y + b16HALF);
  307. /* quad[2] is at the top right of the second trapezoid. quad[1} is
  308. * at the bottom left of the second trapezoid. Interpolate to get
  309. * corresponding point on the right side.
  310. *
  311. * Interpolation is from quad[2] along the line quad[2]->quad[3]
  312. * which as the same slope as the previous interpolation.
  313. */
  314. traps[1].top.x1 = traps[0].bot.x1;
  315. traps[1].top.x2 = traps[0].bot.x2;
  316. traps[1].top.y = traps[0].bot.y;
  317. traps[1].bot.x1 = quad[1].x;
  318. traps[1].bot.x2 = nxgl_interpolate(traps[1].top.x2, quad[1].y - quad[2].y, b16dxdy);
  319. traps[1].bot.y = b16toi(quad[1].y + b16HALF);
  320. }
  321. /* The final trapezond (triangle) at the bottom is new well defined */
  322. traps[2].top.x1 = traps[1].bot.x1;
  323. traps[2].top.x2 = traps[1].bot.x2;
  324. traps[2].top.y = traps[1].bot.y;
  325. traps[2].bot.x1 = quad[3].x;
  326. traps[2].bot.x2 = quad[3].x;
  327. traps[2].bot.y = b16toi(quad[3].y + b16HALF);
  328. }
  329. else
  330. {
  331. /* Get the X positions of each point */
  332. b16x = itob16(line.pt1.x);
  333. quad[0].x = b16x - b16xoffset;
  334. quad[1].x = b16x + b16xoffset;
  335. b16x = itob16(line.pt2.x);
  336. quad[2].x = b16x - b16xoffset;
  337. quad[3].x = b16x + b16xoffset;
  338. gvdbg("Southwest: quad (%08x,%08x),(%08x,%08x),(%08x,%08x),(%08x,%08x)\n",
  339. quad[0].x, quad[0].y, quad[1].x, quad[1].y,
  340. quad[2].x, quad[2].y, quad[3].x, quad[3].y);
  341. /* Now we can form the trapezoids. The top of the first trapezoid
  342. * (triangle) is at quad[0]
  343. */
  344. traps[0].top.x1 = quad[0].x;
  345. traps[0].top.x2 = quad[0].x;
  346. traps[0].top.y = b16toi(quad[0].y + b16HALF);
  347. /* The bottom of the first trapezoid (triangle) may be either at
  348. * quad[1] or quad[2], depending upon orientation.
  349. */
  350. if (quad[1].y < quad[2].y)
  351. {
  352. /* quad[1] is at the bottom right of the triangle. Interpolate
  353. * to get the corresponding point on the left side.
  354. *
  355. * Interpolation is from quad[0] along the line quad[0]->quad[2]
  356. * which as the same slope as the line (negative)
  357. */
  358. b16dxdy = -itob16(iwidth) / iheight;
  359. traps[0].bot.x1 = nxgl_interpolate(traps[0].top.x1, quad[1].y - quad[0].y, b16dxdy);
  360. traps[0].bot.x2 = quad[1].x;
  361. traps[0].bot.y = b16toi(quad[1].y + b16HALF);
  362. /* quad[1] is at the top right of the second trapezoid. quad[2} is
  363. * at the bottom left of the second trapezoid. Interpolate to get
  364. * corresponding point on the right side.
  365. *
  366. * Interpolation is from quad[1] along the line quad[1]->quad[3]
  367. * which as the same slope as the line (negative)
  368. */
  369. traps[1].top.x1 = traps[0].bot.x1;
  370. traps[1].top.x2 = traps[0].bot.x2;
  371. traps[1].top.y = traps[0].bot.y;
  372. traps[1].bot.x1 = quad[2].x;
  373. traps[1].bot.x2 = nxgl_interpolate(traps[1].top.x2, quad[2].y - quad[1].y, b16dxdy);
  374. traps[1].bot.y = b16toi(quad[2].y + b16HALF);
  375. }
  376. else
  377. {
  378. /* quad[2] is at the bottom left of the triangle. Interpolate
  379. * to get the corresponding point on the right side.
  380. *
  381. * Interpolation is from quad[0] along the line quad[0]->quad[1]
  382. * which orthogonal to the slope of the line (and positive)
  383. */
  384. b16dxdy = itob16(iheight) / iwidth;
  385. traps[0].bot.x1 = quad[2].x;
  386. traps[0].bot.x2 = nxgl_interpolate(traps[0].top.x2, quad[2].y - quad[0].y, b16dxdy);
  387. traps[0].bot.y = b16toi(quad[2].y + b16HALF);
  388. /* quad[2] is at the top left of the second trapezoid. quad[1} is
  389. * at the bottom right of the second trapezoid. Interpolate to get
  390. * corresponding point on the left side.
  391. *
  392. * Interpolation is from quad[2] along the line quad[2]->quad[3]
  393. * which as the same slope as the previous interpolation.
  394. */
  395. traps[1].top.x1 = traps[0].bot.x1;
  396. traps[1].top.x2 = traps[0].bot.x2;
  397. traps[1].top.y = traps[0].bot.y;
  398. traps[1].bot.x1 = nxgl_interpolate(traps[1].top.x1, quad[1].y - quad[2].y, b16dxdy);
  399. traps[1].bot.x2 = quad[1].x;
  400. traps[1].bot.y = b16toi(quad[1].y + b16HALF);
  401. }
  402. /* The final trapezond (triangle) at the bottom is new well defined */
  403. traps[2].top.x1 = traps[1].bot.x1;
  404. traps[2].top.x2 = traps[1].bot.x2;
  405. traps[2].top.y = traps[1].bot.y;
  406. traps[2].bot.x1 = quad[3].x;
  407. traps[2].bot.x2 = quad[3].x;
  408. traps[2].bot.y = b16toi(quad[3].y + b16HALF);
  409. }
  410. gvdbg("traps[0]: (%08x,%08x,%d),(%08x,%08x,%d)\n",
  411. traps[0].top.x1, traps[0].top.x2, traps[0].top.y,
  412. traps[0].bot.x1, traps[0].bot.x2, traps[0].bot.y);
  413. gvdbg("traps[1]: (%08x,%08x,%d),(%08x,%08x,%d)\n",
  414. traps[1].top.x1, traps[1].top.x2, traps[1].top.y,
  415. traps[1].bot.x1, traps[1].bot.x2, traps[1].bot.y);
  416. gvdbg("traps[2]: (%08x,%08x,%d),(%08x,%08x,%d)\n",
  417. traps[2].top.x1, traps[2].top.x2, traps[2].top.y,
  418. traps[2].bot.x1, traps[2].bot.x2, traps[2].bot.y);
  419. return 0;
  420. }
  421. /* The line is too vertical to have any significant triangular top or
  422. * bottom. Just return the center parallelogram.
  423. */
  424. traps[1].top.x1 = itob16(line.pt1.x - (linewidth >> 1));
  425. traps[1].top.x2 = traps[1].top.x1 + itob16(linewidth - 1);
  426. traps[1].top.y = line.pt1.y;
  427. traps[1].bot.x1 = itob16(line.pt2.x - (linewidth >> 1));
  428. traps[1].bot.x2 = traps[1].bot.x1 + itob16(linewidth - 1);
  429. traps[1].bot.y = line.pt2.y;
  430. gvdbg("Horizontal traps[1]: (%08x,%08x,%d),(%08x,%08x, %d)\n",
  431. traps[1].top.x1, traps[1].top.x2, traps[1].top.y,
  432. traps[1].bot.x1, traps[1].bot.x2, traps[1].bot.y);
  433. return 1;
  434. }