nxglib_splitline.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /****************************************************************************
  2. * graphics/nxglib/nxglib_splitline.c
  3. *
  4. * Copyright (C) 2011-2012, 2016 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. ginfo("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. ginfo("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. ginfo("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. ginfo("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. else if (linewidth == 1)
  203. {
  204. b16_t pixels_per_row;
  205. /* Close to horizontal line of width 1 */
  206. pixels_per_row = itob16(line.pt2.x - line.pt1.x) /
  207. (line.pt2.y - line.pt1.y);
  208. traps[1].top.x1 = itob16(line.pt1.x);
  209. traps[1].top.x2 = traps[1].top.x1 + pixels_per_row;
  210. traps[1].top.y = line.pt1.y;
  211. traps[1].bot.x2 = itob16(line.pt2.x);
  212. traps[1].bot.x1 = traps[1].bot.x2 - pixels_per_row;
  213. traps[1].bot.y = line.pt2.y;
  214. if (pixels_per_row < 0)
  215. {
  216. b16_t tmp;
  217. tmp = traps[1].top.x2;
  218. traps[1].top.x2 = traps[1].top.x1;
  219. traps[1].top.x1 = tmp;
  220. tmp = traps[1].bot.x2;
  221. traps[1].bot.x2 = traps[1].bot.x1;
  222. traps[1].bot.x1 = tmp;
  223. }
  224. ginfo("Horizontal traps[1]: (%08x,%08x,%d),(%08x,%08x, %d)\n",
  225. traps[1].top.x1, traps[1].top.x2, traps[1].top.y,
  226. traps[1].bot.x1, traps[1].bot.x2, traps[1].bot.y);
  227. return 1;
  228. }
  229. /* Okay, then what remains is interesting.
  230. *
  231. * iheight = |y2 - y1|
  232. * iwidth = |x2 - x1|
  233. */
  234. iheight = line.pt2.y - line.pt1.y + 1;
  235. if (line.pt1.x < line.pt2.x)
  236. {
  237. iwidth = line.pt2.x - line.pt1.x + 1;
  238. }
  239. else
  240. {
  241. iwidth = line.pt1.x - line.pt2.x + 1;
  242. }
  243. /* Applying the line width to the line results in a rotated, rectangle.
  244. * Get the Y offset from an end of the original thin line to a corner of the fat line.
  245. *
  246. * Angle of line: angle = atan2(iheight, iwidth)
  247. * Y offset from line: b16yoffset = linewidth * cos(angle)
  248. *
  249. * For near verical lines, b16yoffset is be nearly zero. For near horizontal
  250. * lines, b16yOffset is be about the same as linewidth.
  251. */
  252. angle = b16atan2(itob16(iheight), itob16(iwidth));
  253. cosangle = b16cos(angle);
  254. b16yoffset = (linewidth * cosangle + 1) >> 1;
  255. /* Get the X offset from an end of the original thin line to a corner of the fat line.
  256. *
  257. * For near vertical lines, b16xoffset is about the same as linewidth. For near
  258. * horizontal lines, b16xoffset is nearly zero.
  259. */
  260. sinangle = b16sin(angle);
  261. b16xoffset = (linewidth * sinangle + 1) >> 1;
  262. ginfo("height: %d width: %d angle: %08x b16yoffset: %08x b16xoffset: %08x\n",
  263. iheight, iwidth, angle, b16yoffset, b16xoffset);
  264. /* Now we know all four points of the rotated rectangle */
  265. iyoffset = b16toi(b16yoffset + b16HALF);
  266. if (iyoffset > 0)
  267. {
  268. /* Get the Y positions of each point */
  269. b16y = itob16(line.pt1.y);
  270. quad[0].y = b16y - b16yoffset;
  271. quad[1].y = b16y + b16yoffset;
  272. b16y = itob16(line.pt2.y);
  273. quad[2].y = b16y - b16yoffset;
  274. quad[3].y = b16y + b16yoffset;
  275. if (line.pt1.x < line.pt2.x)
  276. {
  277. /* Line is going "south east". Get the X positions of each point */
  278. b16x = itob16(line.pt1.x);
  279. quad[0].x = b16x + b16xoffset;
  280. quad[1].x = b16x - b16xoffset;
  281. b16x = itob16(line.pt2.x);
  282. quad[2].x = b16x + b16xoffset;
  283. quad[3].x = b16x - b16xoffset;
  284. ginfo("Southeast: quad (%08x,%08x),(%08x,%08x),(%08x,%08x),(%08x,%08x)\n",
  285. quad[0].x, quad[0].y, quad[1].x, quad[1].y,
  286. quad[2].x, quad[2].y, quad[3].x, quad[3].y);
  287. /* Now we can form the trapezoids. The top of the first trapezoid
  288. * (triangle) is at quad[0]
  289. */
  290. traps[0].top.x1 = quad[0].x;
  291. traps[0].top.x2 = quad[0].x;
  292. traps[0].top.y = b16toi(quad[0].y + b16HALF);
  293. /* The bottom of the first trapezoid (triangle) may be either at
  294. * quad[1] or quad[2], depending upon orientation.
  295. */
  296. if (quad[1]. y < quad[2].y)
  297. {
  298. /* quad[1] is at the bottom left of the triangle. Interpolate
  299. * to get the corresponding point on the right side.
  300. *
  301. * Interpolation is from quad[0] along the line quad[0]->quad[2]
  302. * which as the same slope as the line (positive)
  303. */
  304. b16dxdy = itob16(iwidth) / iheight;
  305. traps[0].bot.x1 = quad[1].x;
  306. traps[0].bot.x2 = nxgl_interpolate(quad[0].x, quad[1].y - quad[0].y, b16dxdy);
  307. traps[0].bot.y = b16toi(quad[1].y + b16HALF);
  308. /* quad[1] is at the top left of the second trapezoid. quad[2} is
  309. * at the bottom right of the second trapezoid. Interpolate to get
  310. * corresponding point on the left side.
  311. *
  312. * Interpolation is from quad[1] along the line quad[1]->quad[3]
  313. * which as the same slope as the line (positive)
  314. */
  315. traps[1].top.x1 = traps[0].bot.x1;
  316. traps[1].top.x2 = traps[0].bot.x2;
  317. traps[1].top.y = traps[0].bot.y;
  318. traps[1].bot.x1 = nxgl_interpolate(traps[1].top.x1, quad[2].y - quad[1].y, b16dxdy);
  319. traps[1].bot.x2 = quad[2].x;
  320. traps[1].bot.y = b16toi(quad[2].y + b16HALF);
  321. }
  322. else
  323. {
  324. /* quad[2] is at the bottom right of the triangle. Interpolate
  325. * to get the corresponding point on the left side.
  326. *
  327. * Interpolation is from quad[0] along the line quad[0]->quad[1]
  328. * which orthogonal to the slope of the line (and negative)
  329. */
  330. b16dxdy = -itob16(iheight) / iwidth;
  331. traps[0].bot.x1 = nxgl_interpolate(quad[0].x, quad[2].y - quad[0].y, b16dxdy);
  332. traps[0].bot.x2 = quad[2].x;
  333. traps[0].bot.y = b16toi(quad[2].y + b16HALF);
  334. /* quad[2] is at the top right of the second trapezoid. quad[1} is
  335. * at the bottom left of the second trapezoid. Interpolate to get
  336. * corresponding point on the right side.
  337. *
  338. * Interpolation is from quad[2] along the line quad[2]->quad[3]
  339. * which as the same slope as the previous interpolation.
  340. */
  341. traps[1].top.x1 = traps[0].bot.x1;
  342. traps[1].top.x2 = traps[0].bot.x2;
  343. traps[1].top.y = traps[0].bot.y;
  344. traps[1].bot.x1 = quad[1].x;
  345. traps[1].bot.x2 = nxgl_interpolate(traps[1].top.x2, quad[1].y - quad[2].y, b16dxdy);
  346. traps[1].bot.y = b16toi(quad[1].y + b16HALF);
  347. }
  348. /* The final trapezond (triangle) at the bottom is new well defined */
  349. traps[2].top.x1 = traps[1].bot.x1;
  350. traps[2].top.x2 = traps[1].bot.x2;
  351. traps[2].top.y = traps[1].bot.y;
  352. traps[2].bot.x1 = quad[3].x;
  353. traps[2].bot.x2 = quad[3].x;
  354. traps[2].bot.y = b16toi(quad[3].y + b16HALF);
  355. }
  356. else
  357. {
  358. /* Get the X positions of each point */
  359. b16x = itob16(line.pt1.x);
  360. quad[0].x = b16x - b16xoffset;
  361. quad[1].x = b16x + b16xoffset;
  362. b16x = itob16(line.pt2.x);
  363. quad[2].x = b16x - b16xoffset;
  364. quad[3].x = b16x + b16xoffset;
  365. ginfo("Southwest: quad (%08x,%08x),(%08x,%08x),(%08x,%08x),(%08x,%08x)\n",
  366. quad[0].x, quad[0].y, quad[1].x, quad[1].y,
  367. quad[2].x, quad[2].y, quad[3].x, quad[3].y);
  368. /* Now we can form the trapezoids. The top of the first trapezoid
  369. * (triangle) is at quad[0]
  370. */
  371. traps[0].top.x1 = quad[0].x;
  372. traps[0].top.x2 = quad[0].x;
  373. traps[0].top.y = b16toi(quad[0].y + b16HALF);
  374. /* The bottom of the first trapezoid (triangle) may be either at
  375. * quad[1] or quad[2], depending upon orientation.
  376. */
  377. if (quad[1].y < quad[2].y)
  378. {
  379. /* quad[1] is at the bottom right of the triangle. Interpolate
  380. * to get the corresponding point on the left side.
  381. *
  382. * Interpolation is from quad[0] along the line quad[0]->quad[2]
  383. * which as the same slope as the line (negative)
  384. */
  385. b16dxdy = -itob16(iwidth) / iheight;
  386. traps[0].bot.x1 = nxgl_interpolate(traps[0].top.x1, quad[1].y - quad[0].y, b16dxdy);
  387. traps[0].bot.x2 = quad[1].x;
  388. traps[0].bot.y = b16toi(quad[1].y + b16HALF);
  389. /* quad[1] is at the top right of the second trapezoid. quad[2} is
  390. * at the bottom left of the second trapezoid. Interpolate to get
  391. * corresponding point on the right side.
  392. *
  393. * Interpolation is from quad[1] along the line quad[1]->quad[3]
  394. * which as the same slope as the line (negative)
  395. */
  396. traps[1].top.x1 = traps[0].bot.x1;
  397. traps[1].top.x2 = traps[0].bot.x2;
  398. traps[1].top.y = traps[0].bot.y;
  399. traps[1].bot.x1 = quad[2].x;
  400. traps[1].bot.x2 = nxgl_interpolate(traps[1].top.x2, quad[2].y - quad[1].y, b16dxdy);
  401. traps[1].bot.y = b16toi(quad[2].y + b16HALF);
  402. }
  403. else
  404. {
  405. /* quad[2] is at the bottom left of the triangle. Interpolate
  406. * to get the corresponding point on the right side.
  407. *
  408. * Interpolation is from quad[0] along the line quad[0]->quad[1]
  409. * which orthogonal to the slope of the line (and positive)
  410. */
  411. b16dxdy = itob16(iheight) / iwidth;
  412. traps[0].bot.x1 = quad[2].x;
  413. traps[0].bot.x2 = nxgl_interpolate(traps[0].top.x2, quad[2].y - quad[0].y, b16dxdy);
  414. traps[0].bot.y = b16toi(quad[2].y + b16HALF);
  415. /* quad[2] is at the top left of the second trapezoid. quad[1} is
  416. * at the bottom right of the second trapezoid. Interpolate to get
  417. * corresponding point on the left side.
  418. *
  419. * Interpolation is from quad[2] along the line quad[2]->quad[3]
  420. * which as the same slope as the previous interpolation.
  421. */
  422. traps[1].top.x1 = traps[0].bot.x1;
  423. traps[1].top.x2 = traps[0].bot.x2;
  424. traps[1].top.y = traps[0].bot.y;
  425. traps[1].bot.x1 = nxgl_interpolate(traps[1].top.x1, quad[1].y - quad[2].y, b16dxdy);
  426. traps[1].bot.x2 = quad[1].x;
  427. traps[1].bot.y = b16toi(quad[1].y + b16HALF);
  428. }
  429. /* The final trapezond (triangle) at the bottom is new well defined */
  430. traps[2].top.x1 = traps[1].bot.x1;
  431. traps[2].top.x2 = traps[1].bot.x2;
  432. traps[2].top.y = traps[1].bot.y;
  433. traps[2].bot.x1 = quad[3].x;
  434. traps[2].bot.x2 = quad[3].x;
  435. traps[2].bot.y = b16toi(quad[3].y + b16HALF);
  436. }
  437. ginfo("traps[0]: (%08x,%08x,%d),(%08x,%08x,%d)\n",
  438. traps[0].top.x1, traps[0].top.x2, traps[0].top.y,
  439. traps[0].bot.x1, traps[0].bot.x2, traps[0].bot.y);
  440. ginfo("traps[1]: (%08x,%08x,%d),(%08x,%08x,%d)\n",
  441. traps[1].top.x1, traps[1].top.x2, traps[1].top.y,
  442. traps[1].bot.x1, traps[1].bot.x2, traps[1].bot.y);
  443. ginfo("traps[2]: (%08x,%08x,%d),(%08x,%08x,%d)\n",
  444. traps[2].top.x1, traps[2].top.x2, traps[2].top.y,
  445. traps[2].bot.x1, traps[2].bot.x2, traps[2].bot.y);
  446. return 0;
  447. }
  448. /* The line is too vertical to have any significant triangular top or
  449. * bottom. Just return the center parallelogram.
  450. */
  451. traps[1].top.x1 = itob16(line.pt1.x - (linewidth >> 1));
  452. traps[1].top.x2 = traps[1].top.x1 + itob16(linewidth - 1);
  453. traps[1].top.y = line.pt1.y;
  454. traps[1].bot.x1 = itob16(line.pt2.x - (linewidth >> 1));
  455. traps[1].bot.x2 = traps[1].bot.x1 + itob16(linewidth - 1);
  456. traps[1].bot.y = line.pt2.y;
  457. ginfo("Horizontal traps[1]: (%08x,%08x,%d),(%08x,%08x, %d)\n",
  458. traps[1].top.x1, traps[1].top.x2, traps[1].top.y,
  459. traps[1].bot.x1, traps[1].bot.x2, traps[1].bot.y);
  460. return 1;
  461. }