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