blob: fcacae51afdb6b43a5d33fdc4584964bfec86471 [file] [log] [blame]
rsc76193d72003-09-30 17:47:42 +00001#include <u.h>
2#include <libc.h>
3#include <draw.h>
4#include <memdraw.h>
5
6typedef struct Seg Seg;
7
8struct Seg
9{
10 Point p0;
11 Point p1;
12 long num;
13 long den;
14 long dz;
15 long dzrem;
16 long z;
17 long zerr;
18 long d;
19};
20
21static void zsort(Seg **seg, Seg **ep);
22static int ycompare(const void*, const void*);
23static int xcompare(const void*, const void*);
24static int zcompare(const void*, const void*);
25static void xscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int, int, int, int);
26static void yscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int, int);
27
28#if 0
29static void
30fillcolor(Memimage *dst, int left, int right, int y, Memimage *src, Point p)
31{
32 int srcval;
33
34 USED(src);
35 srcval = p.x;
36 p.x = left;
37 p.y = y;
38 memset(byteaddr(dst, p), srcval, right-left);
39}
40#endif
41
42static void
43fillline(Memimage *dst, int left, int right, int y, Memimage *src, Point p, int op)
44{
45 Rectangle r;
46
47 r.min.x = left;
48 r.min.y = y;
49 r.max.x = right;
50 r.max.y = y+1;
51 p.x += left;
52 p.y += y;
53 memdraw(dst, r, src, p, memopaque, p, op);
54}
55
56static void
57fillpoint(Memimage *dst, int x, int y, Memimage *src, Point p, int op)
58{
59 Rectangle r;
60
61 r.min.x = x;
62 r.min.y = y;
63 r.max.x = x+1;
64 r.max.y = y+1;
65 p.x += x;
66 p.y += y;
67 memdraw(dst, r, src, p, memopaque, p, op);
68}
69
70void
71memfillpoly(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int op)
72{
73 _memfillpolysc(dst, vert, nvert, w, src, sp, 0, 0, 0, op);
74}
75
76void
77_memfillpolysc(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int detail, int fixshift, int clipped, int op)
78{
79 Seg **seg, *segtab;
80 Point p0;
81 int i;
82
83 if(nvert == 0)
84 return;
85
86 seg = malloc((nvert+2)*sizeof(Seg*));
87 if(seg == nil)
88 return;
89 segtab = malloc((nvert+1)*sizeof(Seg));
90 if(segtab == nil) {
91 free(seg);
92 return;
93 }
94
95 sp.x = (sp.x - vert[0].x) >> fixshift;
96 sp.y = (sp.y - vert[0].y) >> fixshift;
97 p0 = vert[nvert-1];
98 if(!fixshift) {
99 p0.x <<= 1;
100 p0.y <<= 1;
101 }
102 for(i = 0; i < nvert; i++) {
103 segtab[i].p0 = p0;
104 p0 = vert[i];
105 if(!fixshift) {
106 p0.x <<= 1;
107 p0.y <<= 1;
108 }
109 segtab[i].p1 = p0;
110 segtab[i].d = 1;
111 }
112 if(!fixshift)
113 fixshift = 1;
114
115 xscan(dst, seg, segtab, nvert, w, src, sp, detail, fixshift, clipped, op);
116 if(detail)
117 yscan(dst, seg, segtab, nvert, w, src, sp, fixshift, op);
118
119 free(seg);
120 free(segtab);
121}
122
123static long
124mod(long x, long y)
125{
126 long z;
127
128 z = x%y;
rsc26347952005-01-14 03:33:11 +0000129 if((long)(((ulong)z)^((ulong)y)) > 0 || z == 0)
rsc76193d72003-09-30 17:47:42 +0000130 return z;
131 return z + y;
132}
133
134static long
135sdiv(long x, long y)
136{
rsc26347952005-01-14 03:33:11 +0000137 if((long)(((ulong)x)^((ulong)y)) >= 0 || x == 0)
rsc76193d72003-09-30 17:47:42 +0000138 return x/y;
139
140 return (x+((y>>30)|1))/y-1;
141}
142
143static long
144smuldivmod(long x, long y, long z, long *mod)
145{
146 vlong vx;
147
148 if(x == 0 || y == 0){
149 *mod = 0;
150 return 0;
151 }
152 vx = x;
153 vx *= y;
154 *mod = vx % z;
155 if(*mod < 0)
156 *mod += z; /* z is always >0 */
157 if((vx < 0) == (z < 0))
158 return vx/z;
159 return -((-vx)/z);
160}
161
162static void
163xscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int detail, int fixshift, int clipped, int op)
164{
165 long y, maxy, x, x2, xerr, xden, onehalf;
166 Seg **ep, **next, **p, **q, *s;
167 long n, i, iy, cnt, ix, ix2, minx, maxx;
168 Point pt;
169 void (*fill)(Memimage*, int, int, int, Memimage*, Point, int);
170
171 fill = fillline;
172/*
173 * This can only work on 8-bit destinations, since fillcolor is
174 * just using memset on sp.x.
175 *
176 * I'd rather not even enable it then, since then if the general
177 * code is too slow, someone will come up with a better improvement
178 * than this sleazy hack. -rsc
179 *
180 if(clipped && (src->flags&Frepl) && src->depth==8 && Dx(src->r)==1 && Dy(src->r)==1) {
181 fill = fillcolor;
182 sp.x = membyteval(src);
183 }
184 *
185 */
186 USED(clipped);
187
188
189 for(i=0, s=segtab, p=seg; i<nseg; i++, s++) {
190 *p = s;
191 if(s->p0.y == s->p1.y)
192 continue;
193 if(s->p0.y > s->p1.y) {
194 pt = s->p0;
195 s->p0 = s->p1;
196 s->p1 = pt;
197 s->d = -s->d;
198 }
199 s->num = s->p1.x - s->p0.x;
200 s->den = s->p1.y - s->p0.y;
201 s->dz = sdiv(s->num, s->den) << fixshift;
202 s->dzrem = mod(s->num, s->den) << fixshift;
203 s->dz += sdiv(s->dzrem, s->den);
204 s->dzrem = mod(s->dzrem, s->den);
205 p++;
206 }
207 n = p-seg;
208 if(n == 0)
209 return;
210 *p = 0;
211 qsort(seg, p-seg , sizeof(Seg*), ycompare);
212
213 onehalf = 0;
214 if(fixshift)
215 onehalf = 1 << (fixshift-1);
216
217 minx = dst->clipr.min.x;
218 maxx = dst->clipr.max.x;
219
220 y = seg[0]->p0.y;
221 if(y < (dst->clipr.min.y << fixshift))
222 y = dst->clipr.min.y << fixshift;
223 iy = (y + onehalf) >> fixshift;
224 y = (iy << fixshift) + onehalf;
225 maxy = dst->clipr.max.y << fixshift;
226
227 ep = next = seg;
228
229 while(y<maxy) {
230 for(q = p = seg; p < ep; p++) {
231 s = *p;
232 if(s->p1.y < y)
233 continue;
234 s->z += s->dz;
235 s->zerr += s->dzrem;
236 if(s->zerr >= s->den) {
237 s->z++;
238 s->zerr -= s->den;
239 if(s->zerr < 0 || s->zerr >= s->den)
240 print("bad ratzerr1: %ld den %ld dzrem %ld\n", s->zerr, s->den, s->dzrem);
241 }
242 *q++ = s;
243 }
244
245 for(p = next; *p; p++) {
246 s = *p;
247 if(s->p0.y >= y)
248 break;
249 if(s->p1.y < y)
250 continue;
251 s->z = s->p0.x;
252 s->z += smuldivmod(y - s->p0.y, s->num, s->den, &s->zerr);
253 if(s->zerr < 0 || s->zerr >= s->den)
254 print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem);
255 *q++ = s;
256 }
257 ep = q;
258 next = p;
259
260 if(ep == seg) {
261 if(*next == 0)
262 break;
263 iy = (next[0]->p0.y + onehalf) >> fixshift;
264 y = (iy << fixshift) + onehalf;
265 continue;
266 }
267
268 zsort(seg, ep);
269
270 for(p = seg; p < ep; p++) {
271 cnt = 0;
272 x = p[0]->z;
273 xerr = p[0]->zerr;
274 xden = p[0]->den;
275 ix = (x + onehalf) >> fixshift;
276 if(ix >= maxx)
277 break;
278 if(ix < minx)
279 ix = minx;
280 cnt += p[0]->d;
281 p++;
282 for(;;) {
283 if(p == ep) {
284 print("xscan: fill to infinity");
285 return;
286 }
287 cnt += p[0]->d;
288 if((cnt&wind) == 0)
289 break;
290 p++;
291 }
292 x2 = p[0]->z;
293 ix2 = (x2 + onehalf) >> fixshift;
294 if(ix2 <= minx)
295 continue;
296 if(ix2 > maxx)
297 ix2 = maxx;
298 if(ix == ix2 && detail) {
299 if(xerr*p[0]->den + p[0]->zerr*xden > p[0]->den*xden)
300 x++;
301 ix = (x + x2) >> (fixshift+1);
302 ix2 = ix+1;
303 }
304 (*fill)(dst, ix, ix2, iy, src, sp, op);
305 }
306 y += (1<<fixshift);
307 iy++;
308 }
309}
310
311static void
312yscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int fixshift, int op)
313{
314 long x, maxx, y, y2, yerr, yden, onehalf;
315 Seg **ep, **next, **p, **q, *s;
316 int n, i, ix, cnt, iy, iy2, miny, maxy;
317 Point pt;
318
319 for(i=0, s=segtab, p=seg; i<nseg; i++, s++) {
320 *p = s;
321 if(s->p0.x == s->p1.x)
322 continue;
323 if(s->p0.x > s->p1.x) {
324 pt = s->p0;
325 s->p0 = s->p1;
326 s->p1 = pt;
327 s->d = -s->d;
328 }
329 s->num = s->p1.y - s->p0.y;
330 s->den = s->p1.x - s->p0.x;
331 s->dz = sdiv(s->num, s->den) << fixshift;
332 s->dzrem = mod(s->num, s->den) << fixshift;
333 s->dz += sdiv(s->dzrem, s->den);
334 s->dzrem = mod(s->dzrem, s->den);
335 p++;
336 }
337 n = p-seg;
338 if(n == 0)
339 return;
340 *p = 0;
341 qsort(seg, n , sizeof(Seg*), xcompare);
342
343 onehalf = 0;
344 if(fixshift)
345 onehalf = 1 << (fixshift-1);
346
347 miny = dst->clipr.min.y;
348 maxy = dst->clipr.max.y;
349
350 x = seg[0]->p0.x;
351 if(x < (dst->clipr.min.x << fixshift))
352 x = dst->clipr.min.x << fixshift;
353 ix = (x + onehalf) >> fixshift;
354 x = (ix << fixshift) + onehalf;
355 maxx = dst->clipr.max.x << fixshift;
356
357 ep = next = seg;
358
359 while(x<maxx) {
360 for(q = p = seg; p < ep; p++) {
361 s = *p;
362 if(s->p1.x < x)
363 continue;
364 s->z += s->dz;
365 s->zerr += s->dzrem;
366 if(s->zerr >= s->den) {
367 s->z++;
368 s->zerr -= s->den;
369 if(s->zerr < 0 || s->zerr >= s->den)
370 print("bad ratzerr1: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem);
371 }
372 *q++ = s;
373 }
374
375 for(p = next; *p; p++) {
376 s = *p;
377 if(s->p0.x >= x)
378 break;
379 if(s->p1.x < x)
380 continue;
381 s->z = s->p0.y;
382 s->z += smuldivmod(x - s->p0.x, s->num, s->den, &s->zerr);
383 if(s->zerr < 0 || s->zerr >= s->den)
384 print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem);
385 *q++ = s;
386 }
387 ep = q;
388 next = p;
389
390 if(ep == seg) {
391 if(*next == 0)
392 break;
393 ix = (next[0]->p0.x + onehalf) >> fixshift;
394 x = (ix << fixshift) + onehalf;
395 continue;
396 }
397
398 zsort(seg, ep);
399
400 for(p = seg; p < ep; p++) {
401 cnt = 0;
402 y = p[0]->z;
403 yerr = p[0]->zerr;
404 yden = p[0]->den;
405 iy = (y + onehalf) >> fixshift;
406 if(iy >= maxy)
407 break;
408 if(iy < miny)
409 iy = miny;
410 cnt += p[0]->d;
411 p++;
412 for(;;) {
413 if(p == ep) {
414 print("yscan: fill to infinity");
415 return;
416 }
417 cnt += p[0]->d;
418 if((cnt&wind) == 0)
419 break;
420 p++;
421 }
422 y2 = p[0]->z;
423 iy2 = (y2 + onehalf) >> fixshift;
424 if(iy2 <= miny)
425 continue;
426 if(iy2 > maxy)
427 iy2 = maxy;
428 if(iy == iy2) {
429 if(yerr*p[0]->den + p[0]->zerr*yden > p[0]->den*yden)
430 y++;
431 iy = (y + y2) >> (fixshift+1);
432 fillpoint(dst, ix, iy, src, sp, op);
433 }
434 }
435 x += (1<<fixshift);
436 ix++;
437 }
438}
439
440static void
441zsort(Seg **seg, Seg **ep)
442{
443 int done;
444 Seg **q, **p, *s;
445
446 if(ep-seg < 20) {
447 /* bubble sort by z - they should be almost sorted already */
448 q = ep;
449 do {
450 done = 1;
451 q--;
452 for(p = seg; p < q; p++) {
453 if(p[0]->z > p[1]->z) {
454 s = p[0];
455 p[0] = p[1];
456 p[1] = s;
457 done = 0;
458 }
459 }
460 } while(!done);
461 } else {
462 q = ep-1;
463 for(p = seg; p < q; p++) {
464 if(p[0]->z > p[1]->z) {
465 qsort(seg, ep-seg, sizeof(Seg*), zcompare);
466 break;
467 }
468 }
469 }
470}
471
472static int
473ycompare(const void *a, const void *b)
474{
475 Seg **s0, **s1;
476 long y0, y1;
477
478 s0 = (Seg**)a;
479 s1 = (Seg**)b;
480 y0 = (*s0)->p0.y;
481 y1 = (*s1)->p0.y;
482
483 if(y0 < y1)
484 return -1;
485 if(y0 == y1)
486 return 0;
487 return 1;
488}
489
490static int
491xcompare(const void *a, const void *b)
492{
493 Seg **s0, **s1;
494 long x0, x1;
495
496 s0 = (Seg**)a;
497 s1 = (Seg**)b;
498 x0 = (*s0)->p0.x;
499 x1 = (*s1)->p0.x;
500
501 if(x0 < x1)
502 return -1;
503 if(x0 == x1)
504 return 0;
505 return 1;
506}
507
508static int
509zcompare(const void *a, const void *b)
510{
511 Seg **s0, **s1;
512 long z0, z1;
513
514 s0 = (Seg**)a;
515 s1 = (Seg**)b;
516 z0 = (*s0)->z;
517 z1 = (*s1)->z;
518
519 if(z0 < z1)
520 return -1;
521 if(z0 == z1)
522 return 0;
523 return 1;
524}