shithub: city

Download patch

ref: ef9b582868430fefaf733b7dc13ed6e9dc303bac
parent: 70bad7738f3b48f89ed1a624a38109d987bc048e
author: qwx <[email protected]>
date: Tue Oct 5 15:47:50 EDT 2021

draw graphical tiles + fixes

- redraw AFTER loading files
- in-game scaling

the code is based on sce, hopefully somewhat simplified
and made easier to understand.  it's still a huge
complicated mess with a bunch of indirections.  have to
find a way to simplify it.

--- a/city.c
+++ b/city.c
@@ -64,6 +64,7 @@
 	if(proccreate(timeproc, nil, 8192) < 0)
 		sysfatal("init: %r");
 	readfs();
+	resetdraw();
 	startsim();
 	mo.xy = ZP;
 	enum{
@@ -98,6 +99,19 @@
 			case Kdel:
 			case 'q': threadexitsall(nil);
 			case ' ': paused ^= 1; break;
+			case '+':
+			case '=':
+				if(scale < 16){
+					scale++;
+					resetdraw();
+				}
+				break;
+			case '-':
+				if(scale > 1){
+					scale--;
+					resetdraw();
+				}
+				break;
 			}
 			break;
 		case Aanim:
--- a/dat.h
+++ b/dat.h
@@ -9,10 +9,11 @@
 	Tilesz = 32,
 };
 struct Pic{
-	int picw;
-	int pich;
+	Point picsz;
 	u32int *pic;
 };
+
+extern int scale;
 
 enum{
 	Gfish,
--- a/defs.c
+++ b/defs.c
@@ -1,5 +1,6 @@
 #include <u.h>
 #include <libc.h>
+#include <draw.h>
 #include "dat.h"
 
 Good goods[] = {
--- a/drw.c
+++ b/drw.c
@@ -4,24 +4,23 @@
 #include "dat.h"
 #include "fns.h"
 
+int scale = 1;
+Point pan;
 QLock drwlock;
 
-static char *fontname = "/lib/font/bit/fixed/unicode.6x10.font";
-
 static Tile *selected;
 
 enum{
 	Cbg,
-	Cterrain,
-	Cbuild,
 	Ctext,
 	Cend,
 };
 static Image *cols[Cend];
 
-static Rectangle fbr, drwwin, hudr;
+static Rectangle fbr, scrwin, hudr;
+static Point scrofs, tlwin, fbbufr;
 static Image *fb;
-static Point tlsize, tlofs, tlwindow;
+static u32int *fbbuf;
 static int doupdate;
 
 static Image *
@@ -34,12 +33,93 @@
 	return i;
 }
 
+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;
+	}
+}
+
 static Point
+tile2xy(Tile *m)
+{
+	Point p;
+
+	p.x = ((m - map) % mapwidth) * Tilesz;
+	p.y = ((m - map) / mapwidth) * Tilesz;
+	return p;
+}
+
+static Point
 scr2tilexy(Point p)
 {
-	p = subpt(p, drwwin.min);
-	p = Pt(p.x / (tlsize.x + 4), p.y / (tlsize.y + 4));
-	assert(p.x < tlwindow.x && p.x >= 0 && p.y < tlwindow.y && p.y >= 0);
+	p = divpt(subpt(p, scrwin.min), scale * Tilesz);
+	assert(p.x < tlwin.x && p.x >= 0 && p.y < tlwin.y && p.y >= 0);
 	return p;
 }
 
@@ -46,6 +126,7 @@
 static Tile *
 scr2tile(Point p)
 {
+	assert(ptinrect(p, scrwin));
 	p = scr2tilexy(p);
 	return map + p.y * mapwidth + p.x;
 }
@@ -55,10 +136,10 @@
 {
 	char *name;
 
+	draw(screen, hudr, cols[Cbg], nil, ZP);
 	if(selected == nil)
 		return;
 	name = selected->b != nil ? selected->b->name : selected->t->name;
-	draw(screen, hudr, cols[Cbg], nil, ZP);
 	string(screen, hudr.min, cols[Ctext], ZP, font, name);
 }
 
@@ -65,15 +146,10 @@
 static void
 drawtile(Tile *m)
 {
-	int hasbuild;
-	char *s;
-	Point p;
+	Pic *pic;
 
-	p.x = ((m - map) % mapwidth) * (tlsize.x + 4);
-	p.y = ((m - map) / mapwidth) * (tlsize.y + 4);
-	hasbuild = m->b != nil;
-	s = hasbuild ? m->b->abbr : m->t->abbr;
-	string(fb, p, hasbuild ? cols[Cbuild] : cols[Cterrain], ZP, font, s);
+	pic = m->b != nil ? &m->b->Pic : &m->t->Pic;
+	drawpic(tile2xy(m), pic);
 }
 
 void
@@ -83,21 +159,19 @@
 	Tile *m;
 
 	qlock(&drwlock);
-	for(y=0, m=map; y<tlwindow.y; y++){
-		for(x=0; x<tlwindow.x; x++, 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;
 				doupdate = 1;
 			}
-		m += mapwidth - tlwindow.x;
+		m += mapwidth - tlwin.x;
 	}
 	qunlock(&drwlock);
 	if(!doupdate)
 		return;
-	draw(screen, screen->r, fb, nil, tlofs);
-	drawhud();
-	flushimage(display, 1);
+	flushfb();
 	doupdate = 0;
 }
 
@@ -105,7 +179,7 @@
 mouseselect(Point p)
 {
 	doupdate = 1;
-	if(!ptinrect(p, drwwin)){
+	if(!ptinrect(p, scrwin)){
 		selected = nil;
 		return;
 	}
@@ -121,36 +195,71 @@
 	Tile *m;
 
 	draw(fb, fb->r, display->black, nil, ZP);
-	for(y=0, m=map; y<tlwindow.y; y++){
-		for(x=0; x<tlwindow.x; x++, m++)
+	for(y=0, m=map; y<tlwin.y; y++){
+		for(x=0; x<tlwin.x; x++, m++)
 			m->stale = 1;
-		m += mapwidth - tlwindow.x;
+		m += mapwidth - tlwin.x;
 	}
 	updatedraw();
 }
 
 void
+flushfb(void)
+{
+	uchar *p;
+	Rectangle r, r2;
+
+	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);
+}
+
+void
 resetdraw(void)
 {
-	int w, h;
+	int sw, sh;
 
-	w = Dx(screen->r);
-	h = Dy(screen->r);
+	sw = Dx(screen->r);
+	sh = Dy(screen->r);
+	/* fb rect in pixels cropped to fit window */
 	fbr.min = ZP;
-	/* for fuck's sake */
-	fbr.max.x = min(w, tlsize.x * mapwidth + (mapwidth - 1) * 4);
-	fbr.max.y = min(h, tlsize.y * mapheight + (mapheight - 1) * 4 + abs(font->height - font->width));
-	tlofs.x = w > fbr.max.x ? (fbr.max.x - w) / 2 : 0;
-	tlofs.y = h > fbr.max.y ? (fbr.max.y - h) / 2 : 0;
-	tlwindow.x = w > fbr.max.x ? mapwidth : min(mapwidth, fbr.max.x / (tlsize.x + 4) + 1);
-	tlwindow.y = h > fbr.max.y ? mapheight : min(mapheight, fbr.max.y / (tlsize.y + 4) + 1);
-	drwwin.min = subpt(screen->r.min, tlofs);
-	drwwin.max = addpt(drwwin.min, fbr.max);
-	hudr.min = addpt(drwwin.max, Pt(-fbr.max.x - 16, 16));
-	hudr.max = Pt(screen->r.max.x - 16, hudr.min.y + font->height);
+	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(fbr, screen->chan, 0, DNofill);
-	if(!eqpt(tlofs, ZP))
+	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);
 	doupdate = 1;
 	redraw();
@@ -159,18 +268,10 @@
 void
 initdrw(void)
 {
-	if(initdraw(nil, fontname, "city") < 0)
+	if(initdraw(nil, nil, "city") < 0)
 		sysfatal("initdraw: %r");
 	cols[Cbg] = eallocimage(Rect(0,0,1,1), screen->chan, 1, 0x777777FF);
-	cols[Cterrain] = eallocimage(Rect(0,0,1,1), screen->chan, 1, 0x999999FF);
-	cols[Cbuild] = eallocimage(Rect(0,0,1,1), screen->chan, 1, 0x9B6917FF);
 	cols[Ctext] = display->black;
-	tlsize = stringsize(font, "0");
-	if(tlsize.x < tlsize.y)
-		tlsize.y = tlsize.x;
-	else
-		tlsize.x = tlsize.y;
 	fmtinstall('P', Pfmt);
 	fmtinstall('R', Rfmt);
-	resetdraw();
 }
--- a/fns.h
+++ b/fns.h
@@ -7,6 +7,7 @@
 void	initdrw(void);
 void	resetdraw(void);
 void	updatedraw(void);
+void	flushfb(void);
 void	mouseselect(Point);
 int	min(int, int);
 int	max(int, int);
--- a/fs.c
+++ b/fs.c
@@ -27,8 +27,7 @@
 		sysfatal("loadpic %s: inappropriate image format", f);
 	free(f);
 	n = dim * dim;
-	pic->picw = dim;
-	pic->pich = dim;
+	pic->picsz = Pt(dim, dim);
 	pic->pic = emalloc(n * sizeof *pic->pic);
 	n *= i->depth / 8;
 	buf = emalloc(n);
--- a/map.c
+++ b/map.c
@@ -1,5 +1,6 @@
 #include <u.h>
 #include <libc.h>
+#include <draw.h>
 #include "dat.h"
 #include "fns.h"
 
--- a/sim.c
+++ b/sim.c
@@ -1,5 +1,6 @@
 #include <u.h>
 #include <libc.h>
+#include <draw.h>
 #include <thread.h>
 #include "dat.h"
 #include "fns.h"