ref: 07469015a833ea56db430aed28f004e205549360
dir: /drw.c/
#include <u.h> #include <libc.h> #include <draw.h> #include "dat.h" #include "fns.h" Point EP = {-1,-1}; int scale = 1; Point pan; void (*mapdrawfn)(void); QLock drwlock; int repaint; enum{ Cbg, Ctext, Cend, }; static Image *cols[Cend]; Rectangle scrwin; static Rectangle fbr, hudr; static Point scrofs, tlwin, fbbufr; static Image *fb; static u32int *fbbuf; static Image * eallocimage(Rectangle r, ulong chan, int repl, ulong col) { Image *i; if((i = allocimage(display, r, chan, repl, col)) == nil) sysfatal("allocimage: %r"); return i; } Point s2p(Point p) { if(!ptinrect(p, scrwin)) return EP; return divpt(subpt(p, scrwin.min), scale); } Tile * p2t(Point p) { p = divpt(p, Tilesz); assert(p.x >= 0 && p.x < mapwidth && p.y >= 0 && p.y < mapheight); return map + p.y * mapwidth + p.x; } Tile * s2t(Point p) { if(eqpt(p, EP)) return nil; return p2t(p); } static int clippic(Point pp, Pic *pic, Point *scpp, Point *ofs, Point *sz) { int fbbufunsx; Point minp, maxp; fbbufunsx = fbbufr.x / scale; minp = subpt(pp, pan); maxp = addpt(minp, pic->picsz); if(maxp.x < 0 || maxp.y < 0 || minp.x > fbbufunsx || minp.y > fbbufr.y) return -1; ofs->x = minp.x < 0 ? -minp.x : 0; ofs->y = minp.y < 0 ? -minp.y : 0; *sz = subpt(pic->picsz, *ofs); if(maxp.x > fbbufunsx) sz->x -= maxp.x - fbbufunsx; if(maxp.y > fbbufr.y) sz->y -= maxp.y - fbbufr.y; if(sz->x <= 0 || sz->y <= 0) return -1; scpp->x = minp.x + ofs->x; scpp->y = minp.y + ofs->y; return 0; } static void drawpic(Point pp, Pic *pic) { int Δpx, Δx; Point scpp, ofs, sz; u32int v, *p, *fbp, *fbe; if(pic->pic == nil) sysfatal("drawpic: empty pic"); if(clippic(pp, pic, &scpp, &ofs, &sz) < 0) return; p = pic->pic + ofs.y * pic->picsz.x + ofs.x; assert(p < pic->pic + pic->picsz.y * pic->picsz.x); Δpx = pic->picsz.x - sz.x; sz.x *= scale; fbp = fbbuf + scpp.y * fbbufr.x + scpp.x * scale; assert(fbp < fbbuf + fbbufr.x * fbbufr.y); Δx = fbbufr.x - sz.x; while(sz.y-- > 0){ fbe = fbp + sz.x; while(fbp < fbe){ v = *p++; switch(scale){ case 16: fbp[15] = v; case 15: fbp[14] = v; case 14: fbp[13] = v; case 13: fbp[12] = v; case 12: fbp[11] = v; case 11: fbp[10] = v; case 10: fbp[9] = v; case 9: fbp[8] = v; case 8: fbp[7] = v; case 7: fbp[6] = v; case 6: fbp[5] = v; case 5: fbp[4] = v; case 4: fbp[3] = v; case 3: fbp[2] = v; case 2: fbp[1] = v; default: fbp[0] = v; } fbp += scale; } p += Δpx; fbp += Δx; } } void composeat(Tile *m, u32int c) { int k, n, x, w; u32int v, *pp; w = fbr.max.x * fbr.max.y / scale; pp = fbbuf + (m-map) / mapwidth * w + (m-map) % mapwidth * Tilesz * scale; n = Tilesz; w = (fbr.max.x / scale - Tilesz) * scale; while(n-- > 0){ x = Tilesz; while(x-- > 0){ v = *pp; v = (v & 0xff0000) + (c & 0xff0000) >> 1 & 0xff0000 | (v & 0xff00) + (c & 0xff00) >> 1 & 0xff00 | (v & 0xff) + (c & 0xff) >> 1 & 0xff; k = scale; while(k-- > 0) *pp++ = v; } pp += w; } } static void drawhud(void) { char *name; draw(screen, hudr, cols[Cbg], nil, ZP); if(selected == nil) return; name = selected->b != nil ? selected->b->name : selected->t->name; string(screen, hudr.min, cols[Ctext], ZP, font, name); } static void drawmenuselected(void) { int i, n; char s[256], *sp; Point p; Building *b; if(selected == nil) return; assert(selected >= map && selected < map + mapwidth * mapheight); b = buildings + (selected - map); if(b >= buildings + nelem(buildings)){ fprint(2, "nope\n"); return; } p = addpt(hudr.min, Pt(0, font->height)); sp = s; sp = seprint(sp, s+sizeof s, "%s time %d cost ", b->name, b->buildtime); for(i=0, n=0; i<nelem(b->product); i++) if(b->buildcost[i] > 0){ sp = seprint(sp, s+sizeof s, "%s%d %s", n > 0 ? "," : "", b->buildcost[i], goods[i].name); n++; } sp = seprint(sp, s+sizeof s, " product "); for(i=0, n=0; i<nelem(b->product); i++) if(b->product[i] > 0){ sp = seprint(sp, s+sizeof s, "%s%d %s", n > 0 ? "," : "", b->product[i], goods[i].name); n++; } string(screen, p, cols[Ctext], ZP, font, s); } static void drawtile(Tile *m) { Pic *pic; pic = m->b != nil ? &m->b->Pic : &m->t->Pic; drawpic(tile2xy(m), pic); } void drawbuildings(void) { int x, y; Tile *m; Building *b; for(y=0, m=map, b=buildings; y<tlwin.y; y++){ for(x=0; x<tlwin.x; x++, m++, b++){ if(b >= buildings + nelem(buildings)) return; drawpic(tile2xy(m), &b->Pic); if(!couldbuild(b)) composeat(m, 0x555555); if(m->stale){ m->stale = 0; repaint = 1; } } m += mapwidth - tlwin.x; } } static void drawmap(void) { int x, y; Tile *m; for(y=0, m=map; y<tlwin.y; y++){ for(x=0; x<tlwin.x; x++, m++) if(m->stale){ drawtile(m); m->stale = 0; repaint = 1; } m += mapwidth - tlwin.x; } } void drawbuildmenu(void) { drawbuildings(); drawmenuselected(); } static void redrawcanvas(void) { int x, y; Tile *m; memset(fbbuf, 0, fbbufr.x * fbbufr.y * sizeof *fbbuf); for(y=0, m=map; y<tlwin.y; y++){ for(x=0; x<tlwin.x; x++, m++) m->stale = 1; m += mapwidth - tlwin.x; } } void updatedraw(int all) { qlock(&drwlock); if(all || repaint) redrawcanvas(); (mapdrawfn != nil ? mapdrawfn : drawmap)(); qunlock(&drwlock); flushfb(); repaint = 0; } void flushfb(void) { uchar *p; Rectangle r, r2; lockdisplay(display); p = (uchar *)fbbuf; if(scale == 1){ loadimage(fb, fb->r, p, fbbufr.x * fbbufr.y * sizeof *fbbuf); draw(screen, screen->r, fb, nil, scrofs); }else{ r = rectaddpt(fbr, subpt(screen->r.min, scrofs)); r2 = r; while(r.min.y < r2.max.y){ r.max.y = r.min.y + scale; p += loadimage(fb, fb->r, p, fbbufr.x * sizeof *fbbuf); draw(screen, r, fb, nil, ZP); r.min.y = r.max.y; } } drawhud(); flushimage(display, 1); unlockdisplay(display); } void resetdraw(void) { int sw, sh; sw = Dx(screen->r); sh = Dy(screen->r); /* fb rect in pixels cropped to fit window */ fbr.min = ZP; fbr.max.x = min(sw, mapwidth * Tilesz * scale); fbr.max.y = min(sh, mapheight * Tilesz * scale); /* actual drawing fb size, scaled horizontally only */ fbbufr.x = fbr.max.x; fbbufr.y = fbr.max.y / scale; /* negative offset to translate the framebuffer * towards the lower right corner (see draw(2)) */ scrofs.x = -(sw > fbr.max.x ? (sw - fbr.max.x) / 2 : 0); scrofs.y = -(sh > fbr.max.y ? (sh - fbr.max.y) / 2 : 0); /* tile window in tiles cropped to fit window */ tlwin.x = min(mapwidth, fbr.max.x / scale / Tilesz + 1); tlwin.y = min(mapheight, fbr.max.y / scale / Tilesz + 1); /* absolute tile window rect in pixels */ scrwin.min = subpt(screen->r.min, scrofs); scrwin.max = addpt(scrwin.min, fbr.max); /* absolute hud rect in pixels */ hudr.min = addpt(scrwin.max, Pt(-fbr.max.x, font->height)); hudr.max = addpt(hudr.min, Pt(fbr.max.x, screen->r.max.y - hudr.min.y)); freeimage(fb); fb = eallocimage(Rect(0,0,fbr.max.x,scale==1 ? fbr.max.y : 1), XRGB32, scale>1, DNofill); free(fbbuf); fbbuf = emalloc(fbbufr.x * fbbufr.y * sizeof *fbbuf); if(!eqpt(scrofs, ZP)) draw(screen, screen->r, cols[Cbg], nil, ZP); updatedraw(1); } void initdrw(void) { if(initdraw(nil, nil, "city") < 0) sysfatal("initdraw: %r"); display->locking = 1; unlockdisplay(display); cols[Cbg] = eallocimage(Rect(0,0,1,1), screen->chan, 1, 0x777777FF); cols[Ctext] = display->black; fmtinstall('P', Pfmt); fmtinstall('R', Rfmt); }