From 177c18806fd717bfdab44972e29f2cd1b66661de Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Fri, 28 Sep 2018 14:18:21 -0400 Subject: Add psf2png --- bin/.gitignore | 4 +- bin/Makefile | 16 +++++++- bin/README | 1 + bin/man/bin.7 | 5 ++- bin/man/psf2png.1 | 24 ++++++++++++ bin/psf2png.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 bin/man/psf2png.1 create mode 100644 bin/psf2png.c diff --git a/bin/.gitignore b/bin/.gitignore index 6f818aea..03b52195 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -1,7 +1,6 @@ tags scheme.h -fbatt.o -fbclock.o +*.o atch dtch glitch @@ -12,6 +11,7 @@ pbcopy pbd pbpaste pngo +psf2png scheme wake xx diff --git a/bin/Makefile b/bin/Makefile index ef6a1099..e0c60ee7 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -1,6 +1,20 @@ PREFIX = ~/.local -BINS = atch dtch glitch hnel modem open pbcopy pbd pbpaste pngo scheme wake xx +BINS += atch +BINS += dtch +BINS += glitch +BINS += hnel +BINS += modem +BINS += open +BINS += pbcopy +BINS += pbd +BINS += pbpaste +BINS += pngo +BINS += psf2png +BINS += scheme +BINS += wake +BINS += xx + BINS_BSD = watch BINS_LINUX = bri fbatt fbclock psfed BINS_ALL = $(BINS) $(BINS_BSD) $(BINS_LINUX) diff --git a/bin/README b/bin/README index 859819b0..552c9ce8 100644 --- a/bin/README +++ b/bin/README @@ -17,6 +17,7 @@ DESCRIPTION modem(1) fixed baud rate wrapper pbd(1) macOS pasteboard daemon pngo(1) PNG optimizer + psf2png(1) PSF2 to PNG renderer psfed(1) PSF2 font editor wake(1) wake-on-LAN watch(1) watch files diff --git a/bin/man/bin.7 b/bin/man/bin.7 index 383f4bf0..d38e940b 100644 --- a/bin/man/bin.7 +++ b/bin/man/bin.7 @@ -1,4 +1,4 @@ -.Dd September 11, 2018 +.Dd September 28, 2018 .Dt BIN 7 .Os "Causal Agency" . @@ -46,6 +46,9 @@ macOS pasteboard daemon .It Xr pngo 1 PNG optimizer . +.It Xr psf2png 1 +PSF2 to PNG renderer +. .It Xr psfed 1 PSF2 font editor . diff --git a/bin/man/psf2png.1 b/bin/man/psf2png.1 new file mode 100644 index 00000000..3450dce8 --- /dev/null +++ b/bin/man/psf2png.1 @@ -0,0 +1,24 @@ +.Dd September 28, 2018 +.Dt PSF2PNG 1 +.Os "Causal Agency" +. +.Sh NAME +.Nm psf2png +.Nd PSF2 to PNG renderer +. +.Sh SYNOPSIS +.Nm +.Op Ar file +. +.Sh DESCRIPTION +.Nm +renders the PSF2 font +.Ar file +or standard input +to PNG +on standard output. +The glyphs are arranged in 32 columns. +. +.Sh SEE ALSO +.Xr pngo 1 , +.Xr psfed 1 diff --git a/bin/psf2png.c b/bin/psf2png.c new file mode 100644 index 00000000..6106fd89 --- /dev/null +++ b/bin/psf2png.c @@ -0,0 +1,114 @@ +/* Copyright (C) 2018 Curtis McEnroe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const uint32_t GlyphCols = 32; + +static uint32_t crc; +static void pngWrite(const void *ptr, size_t size) { + fwrite(ptr, size, 1, stdout); + if (ferror(stdout)) err(EX_IOERR, "(stdout)"); + crc = crc32(crc, ptr, size); +} +static void pngInt(uint32_t host) { + uint32_t net = htonl(host); + pngWrite(&net, 4); +} +static void pngChunk(const char *type, uint32_t size) { + pngInt(size); + crc = crc32(0, Z_NULL, 0); + pngWrite(type, 4); +} + +int main(int argc, char *argv[]) { + const char *path = NULL; + if (argc > 1) path = argv[1]; + + FILE *file = path ? fopen(path, "r") : stdin; + if (!file) err(EX_NOINPUT, "%s", path); + if (!path) path = "(stdin)"; + + 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; + } header; + size_t len = fread(&header, sizeof(header), 1, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + if (len < 1) errx(EX_DATAERR, "%s: truncated header", path); + + uint32_t widthBytes = (header.glyph.width + 7) / 8; + uint8_t glyphs[header.glyph.len][header.glyph.height][widthBytes]; + len = fread(glyphs, header.glyph.size, header.glyph.len, file); + if (ferror(file)) err(EX_IOERR, "%s", path); + if (len < header.glyph.len) { + errx(EX_DATAERR, "%s: truncated glyphs", path); + } + fclose(file); + + pngWrite("\x89PNG\r\n\x1A\n", 8); + + uint32_t width = header.glyph.width * GlyphCols; + uint32_t rows = (header.glyph.len + GlyphCols - 1) / GlyphCols; + uint32_t height = header.glyph.height * rows; + + pngChunk("IHDR", 13); + pngInt(width); + pngInt(height); + pngWrite("\x08\x00\x00\x00\x00", 5); + pngInt(crc); + + uint8_t data[height][1 + width]; + memset(data, 0, sizeof(data)); + + for (uint32_t i = 0; i < header.glyph.len; ++i) { + uint32_t row = header.glyph.height * (i / GlyphCols); + uint32_t col = 1 + header.glyph.width * (i % GlyphCols); + for (uint32_t y = 0; y < header.glyph.height; ++y) { + for (uint32_t x = 0; x < header.glyph.width; ++x) { + uint8_t bit = glyphs[i][y][x / 8] >> (7 - x % 8) & 1; + data[row + y][col + x] = -bit; + } + } + } + + uLong size = compressBound(sizeof(data)); + uint8_t deflate[size]; + int error = compress(deflate, &size, (Byte *)data, sizeof(data)); + if (error != Z_OK) errx(EX_SOFTWARE, "compress: %d", error); + + pngChunk("IDAT", size); + pngWrite(deflate, size); + pngInt(crc); + + pngChunk("IEND", 0); + pngInt(crc); +} -- cgit 1.4.1