| #include <u.h> |
| #include <libc.h> |
| #include <draw.h> |
| #include <memdraw.h> |
| |
| typedef struct Seg Seg; |
| |
| struct Seg |
| { |
| Point p0; |
| Point p1; |
| long num; |
| long den; |
| long dz; |
| long dzrem; |
| long z; |
| long zerr; |
| long d; |
| }; |
| |
| static void zsort(Seg **seg, Seg **ep); |
| static int ycompare(const void*, const void*); |
| static int xcompare(const void*, const void*); |
| static int zcompare(const void*, const void*); |
| static void xscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int, int, int, int); |
| static void yscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int, int); |
| |
| #if 0 |
| static void |
| fillcolor(Memimage *dst, int left, int right, int y, Memimage *src, Point p) |
| { |
| int srcval; |
| |
| USED(src); |
| srcval = p.x; |
| p.x = left; |
| p.y = y; |
| memset(byteaddr(dst, p), srcval, right-left); |
| } |
| #endif |
| |
| static void |
| fillline(Memimage *dst, int left, int right, int y, Memimage *src, Point p, int op) |
| { |
| Rectangle r; |
| |
| r.min.x = left; |
| r.min.y = y; |
| r.max.x = right; |
| r.max.y = y+1; |
| p.x += left; |
| p.y += y; |
| memdraw(dst, r, src, p, memopaque, p, op); |
| } |
| |
| static void |
| fillpoint(Memimage *dst, int x, int y, Memimage *src, Point p, int op) |
| { |
| Rectangle r; |
| |
| r.min.x = x; |
| r.min.y = y; |
| r.max.x = x+1; |
| r.max.y = y+1; |
| p.x += x; |
| p.y += y; |
| memdraw(dst, r, src, p, memopaque, p, op); |
| } |
| |
| void |
| memfillpoly(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int op) |
| { |
| _memfillpolysc(dst, vert, nvert, w, src, sp, 0, 0, 0, op); |
| } |
| |
| void |
| _memfillpolysc(Memimage *dst, Point *vert, int nvert, int w, Memimage *src, Point sp, int detail, int fixshift, int clipped, int op) |
| { |
| Seg **seg, *segtab; |
| Point p0; |
| int i; |
| |
| if(nvert == 0) |
| return; |
| |
| seg = malloc((nvert+2)*sizeof(Seg*)); |
| if(seg == nil) |
| return; |
| segtab = malloc((nvert+1)*sizeof(Seg)); |
| if(segtab == nil) { |
| free(seg); |
| return; |
| } |
| |
| sp.x = (sp.x - vert[0].x) >> fixshift; |
| sp.y = (sp.y - vert[0].y) >> fixshift; |
| p0 = vert[nvert-1]; |
| if(!fixshift) { |
| p0.x <<= 1; |
| p0.y <<= 1; |
| } |
| for(i = 0; i < nvert; i++) { |
| segtab[i].p0 = p0; |
| p0 = vert[i]; |
| if(!fixshift) { |
| p0.x <<= 1; |
| p0.y <<= 1; |
| } |
| segtab[i].p1 = p0; |
| segtab[i].d = 1; |
| } |
| if(!fixshift) |
| fixshift = 1; |
| |
| xscan(dst, seg, segtab, nvert, w, src, sp, detail, fixshift, clipped, op); |
| if(detail) |
| yscan(dst, seg, segtab, nvert, w, src, sp, fixshift, op); |
| |
| free(seg); |
| free(segtab); |
| } |
| |
| static long |
| mod(long x, long y) |
| { |
| long z; |
| |
| z = x%y; |
| if((long)(((ulong)z)^((ulong)y)) > 0 || z == 0) |
| return z; |
| return z + y; |
| } |
| |
| static long |
| sdiv(long x, long y) |
| { |
| if((long)(((ulong)x)^((ulong)y)) >= 0 || x == 0) |
| return x/y; |
| |
| return (x+((y>>30)|1))/y-1; |
| } |
| |
| static long |
| smuldivmod(long x, long y, long z, long *mod) |
| { |
| vlong vx; |
| |
| if(x == 0 || y == 0){ |
| *mod = 0; |
| return 0; |
| } |
| vx = x; |
| vx *= y; |
| *mod = vx % z; |
| if(*mod < 0) |
| *mod += z; /* z is always >0 */ |
| if((vx < 0) == (z < 0)) |
| return vx/z; |
| return -((-vx)/z); |
| } |
| |
| static void |
| xscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int detail, int fixshift, int clipped, int op) |
| { |
| long y, maxy, x, x2, xerr, xden, onehalf; |
| Seg **ep, **next, **p, **q, *s; |
| long n, i, iy, cnt, ix, ix2, minx, maxx; |
| Point pt; |
| void (*fill)(Memimage*, int, int, int, Memimage*, Point, int); |
| |
| fill = fillline; |
| /* |
| * This can only work on 8-bit destinations, since fillcolor is |
| * just using memset on sp.x. |
| * |
| * I'd rather not even enable it then, since then if the general |
| * code is too slow, someone will come up with a better improvement |
| * than this sleazy hack. -rsc |
| * |
| if(clipped && (src->flags&Frepl) && src->depth==8 && Dx(src->r)==1 && Dy(src->r)==1) { |
| fill = fillcolor; |
| sp.x = membyteval(src); |
| } |
| * |
| */ |
| USED(clipped); |
| |
| |
| for(i=0, s=segtab, p=seg; i<nseg; i++, s++) { |
| *p = s; |
| if(s->p0.y == s->p1.y) |
| continue; |
| if(s->p0.y > s->p1.y) { |
| pt = s->p0; |
| s->p0 = s->p1; |
| s->p1 = pt; |
| s->d = -s->d; |
| } |
| s->num = s->p1.x - s->p0.x; |
| s->den = s->p1.y - s->p0.y; |
| s->dz = sdiv(s->num, s->den) << fixshift; |
| s->dzrem = mod(s->num, s->den) << fixshift; |
| s->dz += sdiv(s->dzrem, s->den); |
| s->dzrem = mod(s->dzrem, s->den); |
| p++; |
| } |
| n = p-seg; |
| if(n == 0) |
| return; |
| *p = 0; |
| qsort(seg, p-seg , sizeof(Seg*), ycompare); |
| |
| onehalf = 0; |
| if(fixshift) |
| onehalf = 1 << (fixshift-1); |
| |
| minx = dst->clipr.min.x; |
| maxx = dst->clipr.max.x; |
| |
| y = seg[0]->p0.y; |
| if(y < (dst->clipr.min.y << fixshift)) |
| y = dst->clipr.min.y << fixshift; |
| iy = (y + onehalf) >> fixshift; |
| y = (iy << fixshift) + onehalf; |
| maxy = dst->clipr.max.y << fixshift; |
| |
| ep = next = seg; |
| |
| while(y<maxy) { |
| for(q = p = seg; p < ep; p++) { |
| s = *p; |
| if(s->p1.y < y) |
| continue; |
| s->z += s->dz; |
| s->zerr += s->dzrem; |
| if(s->zerr >= s->den) { |
| s->z++; |
| s->zerr -= s->den; |
| if(s->zerr < 0 || s->zerr >= s->den) |
| print("bad ratzerr1: %ld den %ld dzrem %ld\n", s->zerr, s->den, s->dzrem); |
| } |
| *q++ = s; |
| } |
| |
| for(p = next; *p; p++) { |
| s = *p; |
| if(s->p0.y >= y) |
| break; |
| if(s->p1.y < y) |
| continue; |
| s->z = s->p0.x; |
| s->z += smuldivmod(y - s->p0.y, s->num, s->den, &s->zerr); |
| if(s->zerr < 0 || s->zerr >= s->den) |
| print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); |
| *q++ = s; |
| } |
| ep = q; |
| next = p; |
| |
| if(ep == seg) { |
| if(*next == 0) |
| break; |
| iy = (next[0]->p0.y + onehalf) >> fixshift; |
| y = (iy << fixshift) + onehalf; |
| continue; |
| } |
| |
| zsort(seg, ep); |
| |
| for(p = seg; p < ep; p++) { |
| cnt = 0; |
| x = p[0]->z; |
| xerr = p[0]->zerr; |
| xden = p[0]->den; |
| ix = (x + onehalf) >> fixshift; |
| if(ix >= maxx) |
| break; |
| if(ix < minx) |
| ix = minx; |
| cnt += p[0]->d; |
| p++; |
| for(;;) { |
| if(p == ep) { |
| print("xscan: fill to infinity"); |
| return; |
| } |
| cnt += p[0]->d; |
| if((cnt&wind) == 0) |
| break; |
| p++; |
| } |
| x2 = p[0]->z; |
| ix2 = (x2 + onehalf) >> fixshift; |
| if(ix2 <= minx) |
| continue; |
| if(ix2 > maxx) |
| ix2 = maxx; |
| if(ix == ix2 && detail) { |
| if(xerr*p[0]->den + p[0]->zerr*xden > p[0]->den*xden) |
| x++; |
| ix = (x + x2) >> (fixshift+1); |
| ix2 = ix+1; |
| } |
| (*fill)(dst, ix, ix2, iy, src, sp, op); |
| } |
| y += (1<<fixshift); |
| iy++; |
| } |
| } |
| |
| static void |
| yscan(Memimage *dst, Seg **seg, Seg *segtab, int nseg, int wind, Memimage *src, Point sp, int fixshift, int op) |
| { |
| long x, maxx, y, y2, yerr, yden, onehalf; |
| Seg **ep, **next, **p, **q, *s; |
| int n, i, ix, cnt, iy, iy2, miny, maxy; |
| Point pt; |
| |
| for(i=0, s=segtab, p=seg; i<nseg; i++, s++) { |
| *p = s; |
| if(s->p0.x == s->p1.x) |
| continue; |
| if(s->p0.x > s->p1.x) { |
| pt = s->p0; |
| s->p0 = s->p1; |
| s->p1 = pt; |
| s->d = -s->d; |
| } |
| s->num = s->p1.y - s->p0.y; |
| s->den = s->p1.x - s->p0.x; |
| s->dz = sdiv(s->num, s->den) << fixshift; |
| s->dzrem = mod(s->num, s->den) << fixshift; |
| s->dz += sdiv(s->dzrem, s->den); |
| s->dzrem = mod(s->dzrem, s->den); |
| p++; |
| } |
| n = p-seg; |
| if(n == 0) |
| return; |
| *p = 0; |
| qsort(seg, n , sizeof(Seg*), xcompare); |
| |
| onehalf = 0; |
| if(fixshift) |
| onehalf = 1 << (fixshift-1); |
| |
| miny = dst->clipr.min.y; |
| maxy = dst->clipr.max.y; |
| |
| x = seg[0]->p0.x; |
| if(x < (dst->clipr.min.x << fixshift)) |
| x = dst->clipr.min.x << fixshift; |
| ix = (x + onehalf) >> fixshift; |
| x = (ix << fixshift) + onehalf; |
| maxx = dst->clipr.max.x << fixshift; |
| |
| ep = next = seg; |
| |
| while(x<maxx) { |
| for(q = p = seg; p < ep; p++) { |
| s = *p; |
| if(s->p1.x < x) |
| continue; |
| s->z += s->dz; |
| s->zerr += s->dzrem; |
| if(s->zerr >= s->den) { |
| s->z++; |
| s->zerr -= s->den; |
| if(s->zerr < 0 || s->zerr >= s->den) |
| print("bad ratzerr1: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); |
| } |
| *q++ = s; |
| } |
| |
| for(p = next; *p; p++) { |
| s = *p; |
| if(s->p0.x >= x) |
| break; |
| if(s->p1.x < x) |
| continue; |
| s->z = s->p0.y; |
| s->z += smuldivmod(x - s->p0.x, s->num, s->den, &s->zerr); |
| if(s->zerr < 0 || s->zerr >= s->den) |
| print("bad ratzerr2: %ld den %ld ratdzrem %ld\n", s->zerr, s->den, s->dzrem); |
| *q++ = s; |
| } |
| ep = q; |
| next = p; |
| |
| if(ep == seg) { |
| if(*next == 0) |
| break; |
| ix = (next[0]->p0.x + onehalf) >> fixshift; |
| x = (ix << fixshift) + onehalf; |
| continue; |
| } |
| |
| zsort(seg, ep); |
| |
| for(p = seg; p < ep; p++) { |
| cnt = 0; |
| y = p[0]->z; |
| yerr = p[0]->zerr; |
| yden = p[0]->den; |
| iy = (y + onehalf) >> fixshift; |
| if(iy >= maxy) |
| break; |
| if(iy < miny) |
| iy = miny; |
| cnt += p[0]->d; |
| p++; |
| for(;;) { |
| if(p == ep) { |
| print("yscan: fill to infinity"); |
| return; |
| } |
| cnt += p[0]->d; |
| if((cnt&wind) == 0) |
| break; |
| p++; |
| } |
| y2 = p[0]->z; |
| iy2 = (y2 + onehalf) >> fixshift; |
| if(iy2 <= miny) |
| continue; |
| if(iy2 > maxy) |
| iy2 = maxy; |
| if(iy == iy2) { |
| if(yerr*p[0]->den + p[0]->zerr*yden > p[0]->den*yden) |
| y++; |
| iy = (y + y2) >> (fixshift+1); |
| fillpoint(dst, ix, iy, src, sp, op); |
| } |
| } |
| x += (1<<fixshift); |
| ix++; |
| } |
| } |
| |
| static void |
| zsort(Seg **seg, Seg **ep) |
| { |
| int done; |
| Seg **q, **p, *s; |
| |
| if(ep-seg < 20) { |
| /* bubble sort by z - they should be almost sorted already */ |
| q = ep; |
| do { |
| done = 1; |
| q--; |
| for(p = seg; p < q; p++) { |
| if(p[0]->z > p[1]->z) { |
| s = p[0]; |
| p[0] = p[1]; |
| p[1] = s; |
| done = 0; |
| } |
| } |
| } while(!done); |
| } else { |
| q = ep-1; |
| for(p = seg; p < q; p++) { |
| if(p[0]->z > p[1]->z) { |
| qsort(seg, ep-seg, sizeof(Seg*), zcompare); |
| break; |
| } |
| } |
| } |
| } |
| |
| static int |
| ycompare(const void *a, const void *b) |
| { |
| Seg **s0, **s1; |
| long y0, y1; |
| |
| s0 = (Seg**)a; |
| s1 = (Seg**)b; |
| y0 = (*s0)->p0.y; |
| y1 = (*s1)->p0.y; |
| |
| if(y0 < y1) |
| return -1; |
| if(y0 == y1) |
| return 0; |
| return 1; |
| } |
| |
| static int |
| xcompare(const void *a, const void *b) |
| { |
| Seg **s0, **s1; |
| long x0, x1; |
| |
| s0 = (Seg**)a; |
| s1 = (Seg**)b; |
| x0 = (*s0)->p0.x; |
| x1 = (*s1)->p0.x; |
| |
| if(x0 < x1) |
| return -1; |
| if(x0 == x1) |
| return 0; |
| return 1; |
| } |
| |
| static int |
| zcompare(const void *a, const void *b) |
| { |
| Seg **s0, **s1; |
| long z0, z1; |
| |
| s0 = (Seg**)a; |
| s1 = (Seg**)b; |
| z0 = (*s0)->z; |
| z1 = (*s1)->z; |
| |
| if(z0 < z1) |
| return -1; |
| if(z0 == z1) |
| return 0; |
| return 1; |
| } |