ref: 8e322e5a5b60c290f6a7cc65f8113bafae36a1e7
dir: /sys/src/games/rotzoomer.c/
/* * rotzoomer - an affinewarp demo */ #include <u.h> #include <libc.h> #include <thread.h> #include <draw.h> #include <mouse.h> #include <keyboard.h> #include <geometry.h> typedef struct Strobos Strobos; typedef struct Backlight Backlight; struct Strobos { Point p; Point txtsz; Image *c; Image *back; Image *txtim; char *txt; Warp txtwp; }; struct Backlight { Image *c; /* bag of colors */ Image *b; /* chosen one */ Warp w; /* roulette */ }; Rectangle UR = {0,0,1,1}; Image *bg; Backlight *bl; Image *sprite; Image *screenb; Mousectl *mctl; Keyboardctl *kctl; Channel *drawc; Warp warp; int nframes; Strobos **strobostab; int nstrobos; int smooth; void * emalloc(ulong size) { void *p; p = malloc(size); if(p == nil) sysfatal("malloc: %r"); setmalloctag(p, getcallerpc(&size)); return p; } Image * eallocimage(Display *disp, Rectangle r, ulong chan, int repl, ulong col) { Image *i; i = allocimage(disp, r, chan, repl, col); if(i == nil) sysfatal("allocimage: %r"); setmalloctag(i, getcallerpc(&disp)); return i; } Image * allocbasketweave(void) { static uchar pattern[] = { 0x07, 0x8B, 0xDD, 0xB8, 0x70, 0xE8, 0xDD, 0x8E, }; Image *i; i = eallocimage(display, Rect(0,0,8,8), GREY1, 1, DNofill); if(loadimage(i, i->r, pattern, 8) != 8) sysfatal("loadimage: %r"); return i; } Backlight * allocbacklight(void) { static ulong pattern[] = { 0x888888, 0xDDDDDD, 0xBBBBBB, 0xFFFFFF, }; Backlight *b; b = emalloc(sizeof(Backlight)); b->c = eallocimage(display, Rect(0,0,2,2), XRGB32, 1, DNofill); b->b = eallocimage(display, UR, XRGB32, 1, DNofill); if(loadimage(b->c, b->c->r, (uchar*)pattern, 2*2*4) != 2*2*4) sysfatal("loadimage: %r"); return b; } void drawstats(void) { static Point sp = {10,10}; char s[128]; snprint(s, sizeof s, "frames %d", nframes); stringbg(screenb, sp, display->white, ZP, font, s, display->black, ZP); } Strobos * allocstrobos(char *txt, ulong col) { Strobos *s; s = emalloc(sizeof(Strobos)); s->c = eallocimage(display, UR, RGBA32, 1, col); s->txt = txt; s->txtsz = stringsize(font, txt); s->txtim = eallocimage(display, Rpt(ZP, s->txtsz), RGBA32, 0, DTransparent); string(s->txtim, ZP, s->c, ZP, font, txt); s->back = eallocimage(display, Rpt(ZP, mulpt(s->txtsz, 4)), RGBA32, 0, DTransparent); return s; } void initstrobos(void) { static struct { char *lbl; ulong col; } tab[] = { "MAKE ME AN OFFER", DPaleyellow, "ONE HUNDRED BILLION DOLLARS", DYellow, "THE PRICEMASTER HAS SPOKEN", DDarkyellow, }; int i; nstrobos = nelem(tab); strobostab = emalloc(nstrobos*sizeof(Strobos *)); for(i = 0; i < nstrobos; i++) strobostab[i] = allocstrobos(tab[i].lbl, setalpha(tab[i].col, 0x7F)); } void drawstrobos(Strobos *s) { double ss, θ; if(nframes % 75 == 0){ /* 2½ seconds */ s->p.x = 50 + frand()*(Dx(screenb->r)-50-50 - Dx(s->back->r)); s->p.y = 50 + frand()*(Dy(screenb->r)-50-50 - Dy(s->back->r)); } if((nframes & 1) == 0){ /* 1/15 second */ ss = (frand() + 0.2)*(4/1.2); θ = (frand()*10 - 10/2)*DEG; Matrix S = { ss, 0, 0, 0, ss, 0, 0, 0, 1, }, R = { cos(θ), -sin(θ), 0, sin(θ), cos(θ), 0, 0, 0, 1, }, T₀ = { 1, 0, -s->txtsz.x/2, 0, 1, -s->txtsz.y/2, 0, 0, 1, }, T₁ = { 1, 0, s->txtsz.x/2, 0, 1, s->txtsz.y/2, 0, 0, 1, }; mulm(R, T₀); mulm(T₁, R); mulm(S, T₁); mkwarp(s->txtwp, S); } affinewarp(s->back, s->back->r, s->txtim, s->txtim->r.min, s->txtwp, smooth); draw(screenb, rectaddpt(s->back->r, addpt(screenb->r.min, s->p)), s->back, nil, ZP); } void redraw(void) { Rectangle cr0; int i; rlockdisplay(display); affinewarp(bl->b, bl->b->r, bl->c, ZP, bl->w, 0); draw(screenb, screenb->r, bl->b, bg, ZP); cr0 = screenb->clipr; replclipr(screenb, 0, insetrect(screenb->r, 50)); affinewarp(screenb, screenb->clipr, sprite, sprite->r.min, warp, smooth); replclipr(screenb, 0, cr0); drawstats(); for(i = 0; i < nstrobos; i++) drawstrobos(strobostab[i]); draw(screen, screen->r, screenb, nil, ZP); flushimage(display, 1); runlockdisplay(display); } void drawproc(void*) { threadsetname("drawproc"); for(;;){ recv(drawc, nil); redraw(); nframes++; } } void update(double f) { Point t; double c, s, ss; c = cos(f); s = sin(f); ss = sin(f*2 + 10); t.x = s*Dx(sprite->r)/2; t.y = 0; Matrix R = { c, -s, 0, s, c, 0, 0, 0, 1, }, S = { ss, 0, 0, 0, ss, 0, 0, 0, 1, }, T₀ = { 1, 0, -Dx(sprite->r)/2, 0, 1, -Dy(sprite->r)/2, 0, 0, 1, }, T₁ = { 1, 0, Dx(sprite->r)/2, 0, 1, Dy(sprite->r)/2, 0, 0, 1, }, T = { 1, 0, t.x, 0, 1, t.y, 0, 0, 1, }; mulm(S, T₀); mulm(R, S); mulm(T₁, R); mulm(T, T₁); mkwarp(warp, T); /* spin it! */ t.x = Dx(bl->b->r)/2; t.y = Dy(bl->b->r)/2; Matrix R₁ = { c, -s, 0, s, c, 0, 0, 0, 1, }; T₀[0][2] = -t.x; T₀[1][2] = -t.y; mulm(R₁, T₀); mkwarp(bl->w, R₁); } void clkproc(void*) { uvlong t0, Δt; threadsetname("clkproc"); for(;;){ t0 = nsec(); update(t0/1e9); nbsend(drawc, nil); Δt = nsec() - t0; if(Δt < 33*1000*1000) sleep(33 - Δt/1000000); } } void mouse(void) { } void key(Rune r) { switch(r){ case Kdel: case 'q': threadexitsall(nil); } } void resize(void) { static Point lastsz; Point cursz; lockdisplay(display); if(getwindow(display, Refnone) < 0) fprint(2, "can't reattach to window\n"); cursz = subpt(screen->r.max, screen->r.min); if(!eqpt(cursz, lastsz)){ freeimage(screenb); screenb = eallocimage(display, Rpt(ZP, cursz), screen->chan, 0, DBlack); lastsz = cursz; } unlockdisplay(display); nbsend(drawc, nil); } void usage(void) { fprint(2, "usage: %s [-s]\n", argv0); exits("usage"); } void threadmain(int argc, char *argv[]) { Rune r; ARGBEGIN{ case 's': smooth++; break; default: usage(); }ARGEND; if(argc != 0) usage(); setfcr(getfcr() &~ FPINVAL); if(initdraw(nil, nil, "rotzoomer") < 0) sysfatal("initdraw: %r"); if((mctl = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); if((kctl = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); bl = allocbacklight(); bg = allocbasketweave(); sprite = display->image; screenb = eallocimage(display, rectsubpt(screen->r, screen->r.min), screen->chan, 0, DBlack); initstrobos(); unlockdisplay(display); drawc = chancreate(sizeof(void*), 1); if(drawc == nil) sysfatal("chancreate: %r"); proccreate(drawproc, nil, mainstacksize); proccreate(clkproc, nil, mainstacksize); enum {MOUSE, RESIZE, KEY}; Alt a[] = { {mctl->c, &mctl->Mouse, CHANRCV}, {mctl->resizec, nil, CHANRCV}, {kctl->c, &r, CHANRCV}, {nil, nil, CHANEND} }; for(;;) switch(alt(a)){ case MOUSE: mouse(); break; case RESIZE: resize(); break; case KEY: key(r); break; } }