line.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. #include "lib9.h"
  2. #include "draw.h"
  3. #include "memdraw.h"
  4. #include "memlayer.h"
  5. enum
  6. {
  7. Arrow1 = 8,
  8. Arrow2 = 10,
  9. Arrow3 = 3,
  10. };
  11. static
  12. int
  13. lmin(int a, int b)
  14. {
  15. if(a < b)
  16. return a;
  17. return b;
  18. }
  19. static
  20. int
  21. lmax(int a, int b)
  22. {
  23. if(a > b)
  24. return a;
  25. return b;
  26. }
  27. /*
  28. * Rather than line clip, we run the Bresenham loop over the full line,
  29. * and clip on each pixel. This is more expensive but means that
  30. * lines look the same regardless of how the windowing has tiled them.
  31. * For speed, we check for clipping outside the loop and make the
  32. * test easy when possible.
  33. */
  34. #ifdef XXX
  35. static
  36. void
  37. horline1(Memimage *dst, Point p0, Point p1, int srcval, Rectangle clipr)
  38. {
  39. int x, y, dy, deltay, deltax, maxx;
  40. int dd, easy, e, bpp, m, m0;
  41. uchar *d;
  42. deltax = p1.x - p0.x;
  43. deltay = p1.y - p0.y;
  44. dd = dst->width*sizeof(ulong);
  45. dy = 1;
  46. if(deltay < 0){
  47. dd = -dd;
  48. deltay = -deltay;
  49. dy = -1;
  50. }
  51. maxx = lmin(p1.x, clipr.max.x-1);
  52. bpp = dst->depth;
  53. m0 = 0xFF^(0xFF>>bpp);
  54. m = m0 >> (p0.x&(7/dst->depth))*bpp;
  55. easy = ptinrect(p0, clipr) && ptinrect(p1, clipr);
  56. e = 2*deltay - deltax;
  57. y = p0.y;
  58. d = byteaddr(dst, p0);
  59. deltay *= 2;
  60. deltax = deltay - 2*deltax;
  61. for(x=p0.x; x<=maxx; x++){
  62. if(easy || (clipr.min.x<=x && clipr.min.y<=y && y<clipr.max.y))
  63. *d ^= (*d^srcval) & m;
  64. if(e > 0){
  65. y += dy;
  66. d += dd;
  67. e += deltax;
  68. }else
  69. e += deltay;
  70. d++;
  71. m >>= bpp;
  72. if(m == 0)
  73. m = m0;
  74. }
  75. }
  76. static
  77. void
  78. verline1(Memimage *dst, Point p0, Point p1, int srcval, Rectangle clipr)
  79. {
  80. int x, y, deltay, deltax, maxy;
  81. int easy, e, bpp, m, m0, dd;
  82. uchar *d;
  83. deltax = p1.x - p0.x;
  84. deltay = p1.y - p0.y;
  85. dd = 1;
  86. if(deltax < 0){
  87. dd = -1;
  88. deltax = -deltax;
  89. }
  90. maxy = lmin(p1.y, clipr.max.y-1);
  91. bpp = dst->depth;
  92. m0 = 0xFF^(0xFF>>bpp);
  93. m = m0 >> (p0.x&(7/dst->depth))*bpp;
  94. easy = ptinrect(p0, clipr) && ptinrect(p1, clipr);
  95. e = 2*deltax - deltay;
  96. x = p0.x;
  97. d = byteaddr(dst, p0);
  98. deltax *= 2;
  99. deltay = deltax - 2*deltay;
  100. for(y=p0.y; y<=maxy; y++){
  101. if(easy || (clipr.min.y<=y && clipr.min.x<=x && x<clipr.max.x))
  102. *d ^= (*d^srcval) & m;
  103. if(e > 0){
  104. x += dd;
  105. d += dd;
  106. e += deltay;
  107. }else
  108. e += deltax;
  109. d += dst->width*sizeof(ulong);
  110. m >>= bpp;
  111. if(m == 0)
  112. m = m0;
  113. }
  114. }
  115. static
  116. void
  117. horliner(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr)
  118. {
  119. int x, y, sx, sy, deltay, deltax, minx, maxx;
  120. int bpp, m, m0;
  121. uchar *d, *s;
  122. deltax = p1.x - p0.x;
  123. deltay = p1.y - p0.y;
  124. sx = drawreplxy(src->r.min.x, src->r.max.x, p0.x+dsrc.x);
  125. minx = lmax(p0.x, clipr.min.x);
  126. maxx = lmin(p1.x, clipr.max.x-1);
  127. bpp = dst->depth;
  128. m0 = 0xFF^(0xFF>>bpp);
  129. m = m0 >> (minx&(7/dst->depth))*bpp;
  130. for(x=minx; x<=maxx; x++){
  131. y = p0.y + (deltay*(x-p0.x)+deltax/2)/deltax;
  132. if(clipr.min.y<=y && y<clipr.max.y){
  133. d = byteaddr(dst, Pt(x, y));
  134. sy = drawreplxy(src->r.min.y, src->r.max.y, y+dsrc.y);
  135. s = byteaddr(src, Pt(sx, sy));
  136. *d ^= (*d^*s) & m;
  137. }
  138. if(++sx >= src->r.max.x)
  139. sx = src->r.min.x;
  140. m >>= bpp;
  141. if(m == 0)
  142. m = m0;
  143. }
  144. }
  145. static
  146. void
  147. verliner(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr)
  148. {
  149. int x, y, sx, sy, deltay, deltax, miny, maxy;
  150. int bpp, m, m0;
  151. uchar *d, *s;
  152. deltax = p1.x - p0.x;
  153. deltay = p1.y - p0.y;
  154. sy = drawreplxy(src->r.min.y, src->r.max.y, p0.y+dsrc.y);
  155. miny = lmax(p0.y, clipr.min.y);
  156. maxy = lmin(p1.y, clipr.max.y-1);
  157. bpp = dst->depth;
  158. m0 = 0xFF^(0xFF>>bpp);
  159. for(y=miny; y<=maxy; y++){
  160. if(deltay == 0) /* degenerate line */
  161. x = p0.x;
  162. else
  163. x = p0.x + (deltax*(y-p0.y)+deltay/2)/deltay;
  164. if(clipr.min.x<=x && x<clipr.max.x){
  165. m = m0 >> (x&(7/dst->depth))*bpp;
  166. d = byteaddr(dst, Pt(x, y));
  167. sx = drawreplxy(src->r.min.x, src->r.max.x, x+dsrc.x);
  168. s = byteaddr(src, Pt(sx, sy));
  169. *d ^= (*d^*s) & m;
  170. }
  171. if(++sy >= src->r.max.y)
  172. sy = src->r.min.y;
  173. }
  174. }
  175. static
  176. void
  177. horline(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr)
  178. {
  179. int x, y, deltay, deltax, minx, maxx;
  180. int bpp, m, m0;
  181. uchar *d, *s;
  182. deltax = p1.x - p0.x;
  183. deltay = p1.y - p0.y;
  184. minx = lmax(p0.x, clipr.min.x);
  185. maxx = lmin(p1.x, clipr.max.x-1);
  186. bpp = dst->depth;
  187. m0 = 0xFF^(0xFF>>bpp);
  188. m = m0 >> (minx&(7/dst->depth))*bpp;
  189. for(x=minx; x<=maxx; x++){
  190. y = p0.y + (deltay*(x-p0.x)+deltay/2)/deltax;
  191. if(clipr.min.y<=y && y<clipr.max.y){
  192. d = byteaddr(dst, Pt(x, y));
  193. s = byteaddr(src, addpt(dsrc, Pt(x, y)));
  194. *d ^= (*d^*s) & m;
  195. }
  196. m >>= bpp;
  197. if(m == 0)
  198. m = m0;
  199. }
  200. }
  201. static
  202. void
  203. verline(Memimage *dst, Point p0, Point p1, Memimage *src, Point dsrc, Rectangle clipr)
  204. {
  205. int x, y, deltay, deltax, miny, maxy;
  206. int bpp, m, m0;
  207. uchar *d, *s;
  208. deltax = p1.x - p0.x;
  209. deltay = p1.y - p0.y;
  210. miny = lmax(p0.y, clipr.min.y);
  211. maxy = lmin(p1.y, clipr.max.y-1);
  212. bpp = dst->depth;
  213. m0 = 0xFF^(0xFF>>bpp);
  214. for(y=miny; y<=maxy; y++){
  215. if(deltay == 0) /* degenerate line */
  216. x = p0.x;
  217. else
  218. x = p0.x + deltax*(y-p0.y)/deltay;
  219. if(clipr.min.x<=x && x<clipr.max.x){
  220. m = m0 >> (x&(7/dst->depth))*bpp;
  221. d = byteaddr(dst, Pt(x, y));
  222. s = byteaddr(src, addpt(dsrc, Pt(x, y)));
  223. *d ^= (*d^*s) & m;
  224. }
  225. }
  226. }
  227. #endif
  228. static Memimage*
  229. membrush(int radius)
  230. {
  231. static Memimage *brush;
  232. static int brushradius;
  233. if(brush==nil || brushradius!=radius){
  234. freememimage(brush);
  235. brush = allocmemimage(Rect(0, 0, 2*radius+1, 2*radius+1), memopaque->chan);
  236. if(brush != nil){
  237. memfillcolor(brush, DTransparent); /* zeros */
  238. memellipse(brush, Pt(radius, radius), radius, radius, -1, memopaque, Pt(radius, radius), S);
  239. }
  240. brushradius = radius;
  241. }
  242. return brush;
  243. }
  244. static
  245. void
  246. discend(Point p, int radius, Memimage *dst, Memimage *src, Point dsrc, int op)
  247. {
  248. Memimage *disc;
  249. Rectangle r;
  250. disc = membrush(radius);
  251. if(disc != nil){
  252. r.min.x = p.x - radius;
  253. r.min.y = p.y - radius;
  254. r.max.x = p.x + radius+1;
  255. r.max.y = p.y + radius+1;
  256. memdraw(dst, r, src, addpt(r.min, dsrc), disc, Pt(0,0), op);
  257. }
  258. }
  259. static
  260. void
  261. arrowend(Point tip, Point *pp, int end, int sin, int cos, int radius)
  262. {
  263. int x1, x2, x3;
  264. /* before rotation */
  265. if(end == Endarrow){
  266. x1 = Arrow1;
  267. x2 = Arrow2;
  268. x3 = Arrow3;
  269. }else{
  270. x1 = (end>>5) & 0x1FF; /* distance along line from end of line to tip */
  271. x2 = (end>>14) & 0x1FF; /* distance along line from barb to tip */
  272. x3 = (end>>23) & 0x1FF; /* distance perpendicular from edge of line to barb */
  273. }
  274. /* comments follow track of right-facing arrowhead */
  275. pp->x = tip.x+((2*radius+1)*sin/2-x1*cos); /* upper side of shaft */
  276. pp->y = tip.y-((2*radius+1)*cos/2+x1*sin);
  277. pp++;
  278. pp->x = tip.x+((2*radius+2*x3+1)*sin/2-x2*cos); /* upper barb */
  279. pp->y = tip.y-((2*radius+2*x3+1)*cos/2+x2*sin);
  280. pp++;
  281. pp->x = tip.x;
  282. pp->y = tip.y;
  283. pp++;
  284. pp->x = tip.x+(-(2*radius+2*x3+1)*sin/2-x2*cos); /* lower barb */
  285. pp->y = tip.y-(-(2*radius+2*x3+1)*cos/2+x2*sin);
  286. pp++;
  287. pp->x = tip.x+(-(2*radius+1)*sin/2-x1*cos); /* lower side of shaft */
  288. pp->y = tip.y+((2*radius+1)*cos/2-x1*sin);
  289. }
  290. void
  291. _memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, Rectangle clipr, int op)
  292. {
  293. int hor;
  294. int sin, cos, dx, dy, t;
  295. Rectangle oclipr, r;
  296. Point q, pts[10], *pp, d;
  297. if(radius < 0)
  298. return;
  299. if(rectclip(&clipr, dst->r) == 0)
  300. return;
  301. if(rectclip(&clipr, dst->clipr) == 0)
  302. return;
  303. d = subpt(sp, p0);
  304. if(rectclip(&clipr, rectsubpt(src->clipr, d)) == 0)
  305. return;
  306. if((src->flags&Frepl)==0 && rectclip(&clipr, rectsubpt(src->r, d))==0)
  307. return;
  308. /* this means that only verline() handles degenerate lines (p0==p1) */
  309. hor = (abs(p1.x-p0.x) > abs(p1.y-p0.y));
  310. /*
  311. * Clipping is a little peculiar. We can't use Sutherland-Cohen
  312. * clipping because lines are wide. But this is probably just fine:
  313. * we do all math with the original p0 and p1, but clip when deciding
  314. * what pixels to draw. This means the layer code can call this routine,
  315. * using clipr to define the region being written, and get the same set
  316. * of pixels regardless of the dicing.
  317. */
  318. if((hor && p0.x>p1.x) || (!hor && p0.y>p1.y)){
  319. q = p0;
  320. p0 = p1;
  321. p1 = q;
  322. t = end0;
  323. end0 = end1;
  324. end1 = t;
  325. }
  326. if((p0.x == p1.x || p0.y == p1.y) && (end0&0x1F) == Endsquare && (end1&0x1F) == Endsquare){
  327. r.min = p0;
  328. r.max = p1;
  329. if(p0.x == p1.x){
  330. r.min.x -= radius;
  331. r.max.x += radius+1;
  332. r.max.y++;
  333. }
  334. else{
  335. r.min.y -= radius;
  336. r.max.y += radius+1;
  337. r.max.x++;
  338. }
  339. oclipr = dst->clipr;
  340. dst->clipr = clipr;
  341. sp = addpt(r.min, d);
  342. memimagedraw(dst, r, src, sp, memopaque, sp, op);
  343. dst->clipr = oclipr;
  344. return;
  345. }
  346. /* Hard: */
  347. /* draw thick line using polygon fill */
  348. icossin2(p1.x-p0.x, p1.y-p0.y, &cos, &sin);
  349. dx = (sin*(2*radius+1))/2;
  350. dy = (cos*(2*radius+1))/2;
  351. pp = pts;
  352. oclipr = dst->clipr;
  353. dst->clipr = clipr;
  354. q.x = ICOSSCALE*p0.x+ICOSSCALE/2-cos/2;
  355. q.y = ICOSSCALE*p0.y+ICOSSCALE/2-sin/2;
  356. switch(end0 & 0x1F){
  357. case Enddisc:
  358. discend(p0, radius, dst, src, d, op);
  359. /* fall through */
  360. case Endsquare:
  361. default:
  362. pp->x = q.x-dx;
  363. pp->y = q.y+dy;
  364. pp++;
  365. pp->x = q.x+dx;
  366. pp->y = q.y-dy;
  367. pp++;
  368. break;
  369. case Endarrow:
  370. arrowend(q, pp, end0, -sin, -cos, radius);
  371. _memfillpolysc(dst, pts, 5, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 1, 10, 1, op);
  372. pp[1] = pp[4];
  373. pp += 2;
  374. }
  375. q.x = ICOSSCALE*p1.x+ICOSSCALE/2+cos/2;
  376. q.y = ICOSSCALE*p1.y+ICOSSCALE/2+sin/2;
  377. switch(end1 & 0x1F){
  378. case Enddisc:
  379. discend(p1, radius, dst, src, d, op);
  380. /* fall through */
  381. case Endsquare:
  382. default:
  383. pp->x = q.x+dx;
  384. pp->y = q.y-dy;
  385. pp++;
  386. pp->x = q.x-dx;
  387. pp->y = q.y+dy;
  388. pp++;
  389. break;
  390. case Endarrow:
  391. arrowend(q, pp, end1, sin, cos, radius);
  392. _memfillpolysc(dst, pp, 5, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 1, 10, 1, op);
  393. pp[1] = pp[4];
  394. pp += 2;
  395. }
  396. _memfillpolysc(dst, pts, pp-pts, ~0, src, addpt(pts[0], mulpt(d, ICOSSCALE)), 0, 10, 1, op);
  397. dst->clipr = oclipr;
  398. return;
  399. }
  400. void
  401. memimageline(Memimage *dst, Point p0, Point p1, int end0, int end1, int radius, Memimage *src, Point sp, int op)
  402. {
  403. _memimageline(dst, p0, p1, end0, end1, radius, src, sp, dst->clipr, op);
  404. }
  405. /*
  406. * Simple-minded conservative code to compute bounding box of line.
  407. * Result is probably a little larger than it needs to be.
  408. */
  409. static
  410. void
  411. addbbox(Rectangle *r, Point p)
  412. {
  413. if(r->min.x > p.x)
  414. r->min.x = p.x;
  415. if(r->min.y > p.y)
  416. r->min.y = p.y;
  417. if(r->max.x < p.x+1)
  418. r->max.x = p.x+1;
  419. if(r->max.y < p.y+1)
  420. r->max.y = p.y+1;
  421. }
  422. int
  423. memlineendsize(int end)
  424. {
  425. int x3;
  426. if((end&0x3F) != Endarrow)
  427. return 0;
  428. if(end == Endarrow)
  429. x3 = Arrow3;
  430. else
  431. x3 = (end>>23) & 0x1FF;
  432. return x3;
  433. }
  434. Rectangle
  435. memlinebbox(Point p0, Point p1, int end0, int end1, int radius)
  436. {
  437. Rectangle r, r1;
  438. int extra;
  439. r.min.x = 10000000;
  440. r.min.y = 10000000;
  441. r.max.x = -10000000;
  442. r.max.y = -10000000;
  443. extra = lmax(memlineendsize(end0), memlineendsize(end1));
  444. r1 = insetrect(canonrect(Rpt(p0, p1)), -(radius+extra));
  445. addbbox(&r, r1.min);
  446. addbbox(&r, r1.max);
  447. return r;
  448. }