ref: 5d4bf57bbda55af5961260e2e03b6518af21c50f
dir: /main.c/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <thread.h>
#include <draw.h>
#include <memdraw.h>
#include <mouse.h>
#include <keyboard.h>
#include <geometry.h>
typedef Point Triangle[3];
Memimage *fb;
Memimage *red, *green, *blue;
Channel *drawc;
void resized(void);
uvlong nanosec(void);
void *
emalloc(ulong n)
{
void *p;
p = malloc(n);
if(p == nil)
sysfatal("malloc: %r");
setmalloctag(p, getcallerpc(&n));
return p;
}
void *
erealloc(void *p, ulong n)
{
void *np;
np = realloc(p, n);
if(np == nil){
if(n == 0)
return nil;
sysfatal("realloc: %r");
}
if(p == nil)
setmalloctag(np, getcallerpc(&p));
else
setrealloctag(np, getcallerpc(&p));
return np;
}
Image *
eallocimage(Display *d, Rectangle r, ulong chan, int repl, ulong col)
{
Image *i;
i = allocimage(d, r, chan, repl, col);
if(i == nil)
sysfatal("allocimage: %r");
return i;
}
Memimage *
eallocmemimage(Rectangle r, ulong chan)
{
Memimage *i;
i = allocmemimage(r, chan);
if(i == nil)
sysfatal("allocmemimage: %r");
memfillcolor(i, DTransparent);
return i;
}
int
min(int a, int b)
{
return a < b? a: b;
}
int
max(int a, int b)
{
return a > b? a: b;
}
Memimage *
rgb(ulong c)
{
Memimage *i;
i = eallocmemimage(Rect(0,0,1,1), screen->chan);
i->flags |= Frepl;
i->clipr = Rect(-1e6, -1e6, 1e6, 1e6);
memfillcolor(i, c);
return i;
}
void
pixel(Memimage *dst, Point p, Memimage *src)
{
Rectangle r;
if(dst == nil || src == nil)
return;
r = rectaddpt(Rect(0,0,1,1), p);
memimagedraw(dst, rectaddpt(r, dst->r.min), src, ZP, nil, ZP, SoverD);
}
void
swap(int *a, int *b)
{
int t;
t = *a;
*a = *b;
*b = t;
}
void
bresenham(Memimage *dst, Point p0, Point p1, Memimage *src)
{
int steep = 0, Δe, e, Δy;
Point p, dp;
/* transpose the points */
if(abs(p0.x-p1.x) < abs(p0.y-p1.y)){
steep = 1;
swap(&p0.x, &p0.y);
swap(&p1.x, &p1.y);
}
/* make them left-to-right */
if(p0.x > p1.x){
swap(&p0.x, &p1.x);
swap(&p0.y, &p1.y);
}
dp = subpt(p1, p0);
Δe = 2*abs(dp.y);
e = 0;
Δy = p1.y > p0.y? 1: -1;
for(p = p0; p.x <= p1.x; p.x++){
if(steep) swap(&p.x, &p.y);
pixel(dst, p, src);
if(steep) swap(&p.x, &p.y);
e += Δe;
if(e > dp.x){
p.y += Δy;
e -= 2*dp.x;
}
}
}
int
ycoordsort(void *a, void *b)
{
return ((Point*)a)->y - ((Point*)b)->y;
}
void
triangle(Memimage *dst, Point p0, Point p1, Point p2, Memimage *src)
{
Triangle t;
t[0] = p0;
t[1] = p1;
t[2] = p2;
qsort(t, nelem(t), sizeof(Point), ycoordsort);
bresenham(dst, t[0], t[1], src);
bresenham(dst, t[1], t[2], src);
bresenham(dst, t[2], t[0], green);
}
void
filltriangle(Memimage *dst, Point p0, Point p1, Point p2, Memimage *src)
{
int y;
double m₀₂, m₀₁, m₁₂;
Point dp₀₂, dp₀₁, dp₁₂;
Triangle t;
t[0] = p0;
t[1] = p1;
t[2] = p2;
qsort(t, nelem(t), sizeof(Point), ycoordsort);
dp₀₂ = subpt(t[2], t[0]);
m₀₂ = (double)dp₀₂.x/dp₀₂.y;
dp₀₁ = subpt(t[1], t[0]);
m₀₁ = (double)dp₀₁.x/dp₀₁.y;
dp₁₂ = subpt(t[2], t[1]);
m₁₂ = (double)dp₁₂.x/dp₁₂.y;
/* first half */
for(y = t[0].y; y <= t[1].y; y++)
bresenham(dst, Pt(t[0].x + (y-t[0].y)*m₀₂,y), Pt(t[0].x + (y-t[0].y)*m₀₁,y), src);
/* second half */
for(; y <= t[2].y; y++)
bresenham(dst, Pt(t[0].x + (y-t[0].y)*m₀₂,y), Pt(t[1].x + (y-t[1].y)*m₁₂,y), src);
}
void
shade(Memimage *dst, Rectangle r, Memimage *(*shader)(Point))
{
Point p;
Memimage *c;
for(p.y = r.min.y; p.y < r.max.y; p.y++)
for(p.x = r.min.x; p.x < r.max.x; p.x++)
if((c = shader(p)) != nil)
pixel(dst, p, c);
}
Memimage *
triangleshader(Point p)
{
Triangle2 t;
Rectangle bbox;
Point3 bc;
uchar cbuf[4];
t.p0 = Pt2(240,200,1);
t.p1 = Pt2(400,40,1);
t.p2 = Pt2(240,40,1);
bbox = Rect(
min(min(t.p0.x, t.p1.x), t.p2.x), min(min(t.p0.y, t.p1.y), t.p2.y),
max(max(t.p0.x, t.p1.x), t.p2.x), max(max(t.p0.y, t.p1.y), t.p2.y)
);
if(!ptinrect(p, bbox))
return nil;
bc = barycoords(t, Pt2(p.x,p.y,1));
if(bc.x < 0 || bc.y < 0 || bc.z < 0)
return nil;
cbuf[0] = 0xFF;
cbuf[1] = 0xFF*bc.z;
cbuf[2] = 0xFF*bc.y;
cbuf[3] = 0xFF*bc.x;
memfillcolor(red, *(ulong*)cbuf);
return red;
}
Memimage *
circleshader(Point p)
{
Point2 uv;
double r;
uchar cbuf[4];
uv = Pt2(p.x,p.y,1);
uv.x /= Dx(fb->r);
uv.y /= Dy(fb->r);
r = 0.3;
if(vec2len(subpt2(uv, Vec2(0.5,0.5))) > r)
return nil;
cbuf[0] = 0xFF;
cbuf[1] = 0;
cbuf[2] = 0xFF*uv.x;
cbuf[3] = 0xFF*uv.y;
memfillcolor(red, *(ulong*)cbuf);
return red;
}
void
redraw(void)
{
lockdisplay(display);
draw(screen, screen->r, display->black, nil, ZP);
loadimage(screen, fb->r, byteaddr(fb, fb->r.min), bytesperline(fb->r, fb->depth)*Dy(fb->r));
flushimage(display, 1);
unlockdisplay(display);
}
void
rmb(Mousectl *, Keyboardctl *)
{
}
void
lmb(Mousectl *, Keyboardctl *)
{
}
void
mouse(Mousectl *mc, Keyboardctl *kc)
{
if((mc->buttons&1) != 0)
lmb(mc, kc);
if((mc->buttons&4) != 0)
rmb(mc, kc);
}
void
key(Rune r)
{
switch(r){
case Kdel:
case 'q':
threadexitsall(nil);
}
}
void
usage(void)
{
fprint(2, "usage: %s\n", argv0);
exits("usage");
}
void
threadmain(int argc, char *argv[])
{
Mousectl *mc;
Keyboardctl *kc;
Rune r;
uvlong t0, t1;
GEOMfmtinstall();
ARGBEGIN{
default: usage();
}ARGEND;
if(argc > 0)
usage();
if(newwindow(nil) < 0)
sysfatal("newwindow: %r");
if(initdraw(nil, nil, nil) < 0)
sysfatal("initdraw: %r");
if(memimageinit() != 0)
sysfatal("memimageinit: %r");
if((mc = initmouse(nil, screen)) == nil)
sysfatal("initmouse: %r");
if((kc = initkeyboard(nil)) == nil)
sysfatal("initkeyboard: %r");
fb = eallocmemimage(screen->r, screen->chan);
red = rgb(DRed);
green = rgb(DGreen);
blue = rgb(DBlue);
t0 = nanosec();
shade(fb, rectsubpt(fb->r, fb->r.min), circleshader);
t1 = nanosec();
fprint(2, "shader took %lludns\n", t1-t0);
bresenham(fb, Pt(40,40), Pt(300,300), red);
bresenham(fb, Pt(80,80), Pt(100,200), red);
bresenham(fb, Pt(80,80), Pt(200,100), red);
filltriangle(fb, Pt(30,10), Pt(45, 45), Pt(5, 100), blue);
triangle(fb, Pt(30,10), Pt(45, 45), Pt(5, 100), red);
filltriangle(fb, Pt(300,120), Pt(200,350), Pt(50, 210), blue);
triangle(fb, Pt(300,120), Pt(200,350), Pt(50, 210), red);
filltriangle(fb, Pt(400,230), Pt(450,180), Pt(150, 320), blue);
triangle(fb, Pt(400,230), Pt(450,180), Pt(150, 320), red);
t0 = nanosec();
shade(fb, rectsubpt(fb->r, fb->r.min), triangleshader);
t1 = nanosec();
fprint(2, "shader took %lludns\n", t1-t0);
drawc = chancreate(sizeof(void*), 1);
display->locking = 1;
unlockdisplay(display);
nbsend(drawc, nil);
for(;;){
enum { MOUSE, RESIZE, KEYBOARD, DRAW };
Alt a[] = {
{mc->c, &mc->Mouse, CHANRCV},
{mc->resizec, nil, CHANRCV},
{kc->c, &r, CHANRCV},
{drawc, nil, CHANRCV},
{nil, nil, CHANEND}
};
switch(alt(a)){
case MOUSE:
mouse(mc, kc);
break;
case RESIZE:
resized();
break;
case KEYBOARD:
key(r);
break;
case DRAW:
redraw();
break;
}
}
}
void
resized(void)
{
lockdisplay(display);
if(getwindow(display, Refnone) < 0)
sysfatal("couldn't resize");
unlockdisplay(display);
nbsend(drawc, nil);
}