about summary refs log tree commit diff homepage
path: root/image.c
diff options
context:
space:
mode:
Diffstat (limited to 'image.c')
-rw-r--r--image.c169
1 files changed, 102 insertions, 67 deletions
diff --git a/image.c b/image.c
index ef583e9..d67706f 100644
--- a/image.c
+++ b/image.c
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/stat.h>
 #include <sysexits.h>
 #include <unistd.h>
 #include <zlib.h>
@@ -47,6 +48,64 @@ static const uint8_t Palette[16][3] = {
 	{ 0xFF, 0xFF, 0xFF },
 };
 
+static struct {
+	uint32_t magic;
+	uint32_t version;
+	uint32_t size;
+	uint32_t flags;
+	struct {
+		uint32_t len;
+		uint32_t size;
+		uint32_t height;
+		uint32_t width;
+	} glyph;
+} font;
+
+static uint8_t *glyphs;
+
+static void fontLoad(const char *path) {
+	FILE *file = fopen(path, "r");
+	if (!file) err(EX_NOINPUT, "%s", path);
+
+	size_t len = fread(&font, sizeof(font), 1, file);
+	if (ferror(file)) err(EX_IOERR, "%s", path);
+	if (len < 1) errx(EX_DATAERR, "%s: truncated header", path);
+
+	if (font.magic != 0x864AB572 || font.size != sizeof(font)) {
+		errx(EX_DATAERR, "%s: invalid header", path);
+	}
+
+	glyphs = calloc(font.glyph.len, font.glyph.size);
+	if (!glyphs) err(EX_OSERR, "calloc");
+
+	len = fread(glyphs, font.glyph.size, font.glyph.len, file);
+	if (ferror(file)) err(EX_IOERR, "%s", path);
+	if (len < font.glyph.len) errx(EX_DATAERR, "%s: truncated glyphs", path);
+	fclose(file);
+}
+
+static struct Tile (*tiles)[TileRows][TileCols];
+
+static void tilesMap(const char *path) {
+	int fd = open(path, O_RDONLY);
+	if (fd < 0) err(EX_NOINPUT, "%s", path);
+
+	struct stat stat;
+	int error = fstat(fd, &stat);
+	if (error) err(EX_IOERR, "%s", path);
+
+	if ((size_t)stat.st_size < TilesSize) {
+		errx(EX_DATAERR, "%s: truncated tiles", path);
+	}
+
+	tiles = mmap(NULL, TilesSize, PROT_READ, MAP_SHARED, fd, 0);
+	if (tiles == MAP_FAILED) err(EX_OSERR, "mmap");
+	close(fd);
+
+	error = madvise(tiles, TilesSize, MADV_RANDOM);
+	if (error) err(EX_OSERR, "madvise");
+}
+
 static uint32_t crc;
 static void pngWrite(const void *ptr, size_t size) {
 	fwrite(ptr, size, 1, stdout);
@@ -63,64 +122,12 @@ static void pngChunk(char type[static 4], uint32_t size) {
 	pngWrite(type, 4);
 }
 
-int main(int argc, char *argv[]) {
-	const char *fontPath = "default8x16.psfu";
-	const char *dataPath = "torus.dat";
-	uint32_t tileX = TileInitX;
-	uint32_t tileY = TileInitY;
-
-	int opt;
-	while (0 < (opt = getopt(argc, argv, "d:f:x:y:"))) {
-		switch (opt) {
-			break; case 'd': dataPath = optarg;
-			break; case 'f': fontPath = optarg;
-			break; case 'x': tileX = strtoul(optarg, NULL, 0) % TileCols;
-			break; case 'y': tileY = strtoul(optarg, NULL, 0) % TileRows;
-			break; default:  return EX_USAGE;
-		}
-	}
-
-	FILE *file = fopen(fontPath, "r");
-	if (!file) err(EX_NOINPUT, "%s", fontPath);
-
-	struct {
-		uint32_t magic;
-		uint32_t version;
-		uint32_t size;
-		uint32_t flags;
-		struct {
-			uint32_t len;
-			uint32_t size;
-			uint32_t height;
-			uint32_t width;
-		} glyph;
-	} psf;
-	size_t len = fread(&psf, sizeof(psf), 1, file);
-	if (ferror(file)) err(EX_IOERR, "%s", fontPath);
-	if (len < 1) errx(EX_DATAERR, "%s: truncated header", fontPath);
-
-	uint8_t glyphs[psf.glyph.len][psf.glyph.height][(psf.glyph.width + 7) / 8];
-	len = fread(glyphs, psf.glyph.size, psf.glyph.len, file);
-	if (ferror(file)) err(EX_IOERR, "%s", fontPath);
-	if (len < 1) errx(EX_DATAERR, "%s: truncated glyphs", fontPath);
-	fclose(file);
-
-	int fd = open(dataPath, O_RDONLY);
-	if (fd < 0) err(EX_NOINPUT, "%s", dataPath);
-
-	struct Tile *tile = mmap(
-		NULL, sizeof(struct Tile),
-		PROT_READ, MAP_SHARED,
-		fd, sizeof(struct Tile) * (TileRows * tileY + tileX)
-	);
-	if (tile == MAP_FAILED) err(EX_IOERR, "mmap");
-	close(fd);
+static void render(uint32_t tileX, uint32_t tileY) {
+	uint32_t width = CellCols * font.glyph.width;
+	uint32_t height = CellRows * font.glyph.height;
 
 	pngWrite("\x89PNG\r\n\x1A\n", 8);
 
-	uint32_t width = CellCols * psf.glyph.width;
-	uint32_t height = CellRows * psf.glyph.height;
-
 	pngChunk("IHDR", 13);
 	pngInt(width);
 	pngInt(height);
@@ -134,18 +141,23 @@ int main(int argc, char *argv[]) {
 	uint8_t data[height][1 + width];
 	memset(data, 0, sizeof(data));
 
-	for (uint32_t y = 0; y < CellRows; ++y) {
-		for (uint32_t x = 0; x < CellCols; ++x) {
-			uint8_t cell = tile->cells[y][x];
-			uint8_t fg = tile->colors[y][x] & 0x0F;
-			uint8_t bg = tile->colors[y][x] >> 4;
-
-			uint32_t row = psf.glyph.height * y;
-			uint32_t col = psf.glyph.width * x;
-			for (uint8_t gy = 0; gy < psf.glyph.height; ++gy) {
-				for (uint8_t gx = 0; gx < psf.glyph.width; ++gx) {
-					uint8_t bit = glyphs[cell][gy][gx / 8] >> (7 - gx % 8) & 1;
-					data[row + gy][1 + col + gx] = (bit ? fg : bg);
+	uint32_t widthBytes = (font.glyph.width + 7) / 8;
+	uint8_t (*bits)[font.glyph.len][font.glyph.height][widthBytes];
+	bits = (void *)glyphs;
+
+	struct Tile *tile = &(*tiles)[tileY][tileX];
+	for (uint32_t cellY = 0; cellY < CellRows; ++cellY) {
+		for (uint32_t cellX = 0; cellX < CellCols; ++cellX) {
+			uint8_t cell = tile->cells[cellY][cellX];
+			uint8_t fg = tile->colors[cellY][cellX] & 0x0F;
+			uint8_t bg = tile->colors[cellY][cellX] >> 4;
+
+			uint32_t glyphX = font.glyph.width * cellX;
+			uint32_t glyphY = font.glyph.height * cellY;
+			for (uint32_t y = 0; y < font.glyph.height; ++y) {
+				for (uint32_t x = 0; x < font.glyph.width; ++x) {
+					uint8_t bit = (*bits)[cell][y][x / 8] >> (7 - x % 8) & 1;
+					data[glyphY + y][1 + glyphX + x] = (bit ? fg : bg);
 				}
 			}
 		}
@@ -162,6 +174,29 @@ int main(int argc, char *argv[]) {
 
 	pngChunk("IEND", 0);
 	pngInt(crc);
+}
+
+int main(int argc, char *argv[]) {
+	const char *fontPath = "default8x16.psfu";
+	const char *dataPath = "torus.dat";
+	uint32_t tileX = TileInitX;
+	uint32_t tileY = TileInitY;
+
+	int opt;
+	while (0 < (opt = getopt(argc, argv, "d:f:x:y:"))) {
+		switch (opt) {
+			break; case 'd': dataPath = optarg;
+			break; case 'f': fontPath = optarg;
+			break; case 'x': tileX = strtoul(optarg, NULL, 0) % TileCols;
+			break; case 'y': tileY = strtoul(optarg, NULL, 0) % TileRows;
+			break; default:  return EX_USAGE;
+		}
+	}
+
+	fontLoad(fontPath);
+	tilesMap(dataPath);
+
+	render(tileX, tileY);
 
 	return EX_OK;
 }