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"