Last active
September 8, 2024 05:59
-
-
Save katef/59450aa622315bd35fc27bd383c2dbe6 to your computer and use it in GitHub Desktop.
XBM to UTF-8 braille image things
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* John Conway's Game of Life. | |
* | |
* This is written for POSIX, using Curses. Resizing of the terminal is not | |
* supported. | |
* | |
* By convention in this program, x is the horizontal coordinate and y is | |
* vertical. There correspond to the width and height respectively. | |
* The current generation number is illustrated when show_generation is set. | |
* | |
* You'll need ncurses to compile. On debian this is packaged as: | |
* | |
* $ apt-get install libncurses-dev | |
* | |
* One way to make a suitable image: | |
* | |
* $ convert -scale x100 snorkmaiden.png /tmp/snorkmaiden.xbm | |
* | |
* I didn't write runtime image loading code. Images are given at compile time. | |
* XBM is very simple, and the source is an array in C code. | |
* Inside an .xbm file looks something like this, where the data is named | |
* after the filename (in this case snorkmaiden.xbm): | |
* | |
* #define snorkmaiden_width 57 | |
* #define snorkmaiden_height 61 | |
* static char snorkmaiden_bits[] = { 0xab, 0xcd, ... } | |
* | |
* To compile you need to pass both the filename and the name for the variables, | |
* sorry about that. | |
* | |
* $ gcc -DIMAGE_NAME=snorkmaiden -DIMAGE_XBM='"/tmp/snorkmaiden.xbm"' life-utf8.c -lncursesw | |
* | |
* You can record your terminal to an animated gif by running | |
* xwininfo to find the coordinates for your window, and then: | |
* | |
* byzanz-record -d 30 -x 1056 -y 20 -w 864 -h 795 /tmp/x.gif | |
* | |
* Public domain, have fun. | |
* Katherine Flavel, @thingskatedid | |
*/ | |
#define _XOPEN_SOURCE 500 | |
#include <curses.h> | |
#include <unistd.h> | |
#include <assert.h> | |
#include <locale.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <limits.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#include <float.h> | |
#define paste_(a, b) a##b | |
#define paste(a, b) paste_(a, b) | |
#ifdef IMAGE_XBM | |
#include IMAGE_XBM | |
#endif | |
static unsigned w; | |
static unsigned h; | |
static unsigned char *board; | |
static int cursor; /* visibility */ | |
/* Configurable things */ | |
static int invert; | |
static int show_generation; | |
static int step; | |
static int persistence; | |
static double delay = 500000; | |
static double decay = 0.8; | |
static unsigned char * | |
cell(unsigned x, unsigned y) | |
{ | |
return &board[y * w + x]; | |
} | |
static void | |
utf8(unsigned cp, char c[]) | |
{ | |
if (cp <= 0x7f) { | |
c[0] = cp; | |
return; | |
} | |
if (cp <= 0x7ff) { | |
c[0] = (cp >> 6) + 192; | |
c[1] = (cp & 63) + 128; | |
return; | |
} | |
if (0xd800 <= cp && cp <= 0xdfff) { | |
/* invalid */ | |
goto error; | |
} | |
if (cp <= 0xffff) { | |
c[0] = (cp >> 12) + 224; | |
c[1] = ((cp >> 6) & 63) + 128; | |
c[2] = (cp & 63) + 128; | |
return; | |
} | |
if (cp <= 0x10ffff) { | |
c[0] = (cp >> 18) + 240; | |
c[1] = ((cp >> 12) & 63) + 128; | |
c[2] = ((cp >> 6) & 63) + 128; | |
c[3] = (cp & 63) + 128; | |
return; | |
} | |
error: | |
fprintf(stderr, "codepoint out of range\n"); | |
exit(1); | |
} | |
static void | |
print(const unsigned char *board, unsigned long g) | |
{ | |
unsigned x; | |
unsigned y; | |
struct { | |
int x; | |
int y; | |
int m; | |
} braille[] = { | |
{ 0, 0, 0x2801 }, { 1, 0, 0x2808 }, | |
{ 0, 1, 0x2802 }, { 1, 1, 0x2810 }, | |
{ 0, 2, 0x2804 }, { 1, 2, 0x2820 }, | |
{ 0, 3, 0x2840 }, { 1, 3, 0x2880 } | |
}; | |
for (y = 0; y < h - 3; y += 4) { | |
for (x = 0; x < w - 1; x += 2) { | |
char s[5] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
size_t i; | |
int cp; | |
cp = 0x2800; /* blank */ | |
for (i = 0; i < sizeof braille / sizeof *braille; i++) { | |
if (*cell(x + braille[i].x, y + braille[i].y)) { | |
cp |= braille[i].m; | |
} | |
} | |
utf8(cp, s); | |
mvaddstr(y / 4, x / 2, s); | |
} | |
} | |
if (show_generation) { | |
mvprintw(0, 0, "%ld ", g); | |
} | |
refresh(); | |
} | |
static int | |
xpm_pixel(unsigned x, unsigned y, unsigned width, unsigned height, const unsigned char *img) | |
{ | |
unsigned byte, bit; | |
/* xpm has eight bits per element. Rows are padded to a byte's width */ | |
width += 7; | |
width &= ~7; | |
byte = (y * width + x) / 8; | |
bit = (y * width + x) % 8; | |
return !!(img[byte] & (1U << bit)); | |
} | |
static void | |
fill_img(unsigned char *board, unsigned width, unsigned height, const void *img) | |
{ | |
unsigned x, y; | |
int pixel; | |
assert(width <= w); | |
assert(width <= h); | |
assert(img != NULL); | |
for (y = 0; y < height; y++) { | |
for (x = 0; x < width; x++) { | |
pixel = xpm_pixel(x, y, width, height, img); | |
if (invert) { | |
pixel = !pixel; | |
} | |
if (pixel) { | |
*cell(x, y) = pixel; | |
} | |
} | |
} | |
} | |
static void | |
fill_rpentomino(unsigned char *board) | |
{ | |
unsigned x; | |
unsigned y; | |
unsigned i; | |
struct { | |
int x; | |
int y; | |
} r[] = { | |
{ 1, 0 }, { 2, 0 }, | |
{ 0, 1 }, { 1, 1 }, | |
{ 1, 2 } | |
}; | |
for (i = 0; i < sizeof r / sizeof *r; i++) { | |
x = r[i].x + w / 2; | |
y = r[i].y + h / 2; | |
*cell(x, y) = 1; | |
} | |
} | |
static void | |
fill(unsigned char *board) | |
{ | |
(void) fill_img; | |
(void) fill_rpentomino; | |
#ifndef IMAGE_XBM | |
fill_rpentomino(board); | |
#else | |
fill_img(board, paste(IMAGE_NAME, _width), paste(IMAGE_NAME, _height), paste(IMAGE_NAME, _bits)); | |
#endif | |
} | |
static unsigned | |
wrap(unsigned range, int i) | |
{ | |
if (i < 0) { | |
i += range; | |
} | |
return i % range; | |
} | |
static void | |
update(unsigned char *board) | |
{ | |
unsigned x; | |
unsigned y; | |
struct { | |
int x; | |
int y; | |
} moore[] = { | |
{ -1, -1 }, { 0, -1 }, { 1, -1 }, | |
{ -1, 0 }, { 1, 0 }, | |
{ -1, 1 }, { 0, 1 }, { 1, 1 } | |
}; | |
for (y = 0; y < h; y++) { | |
for (x = 0; x < w; x++) { | |
unsigned char v; | |
unsigned count; | |
unsigned n; | |
count = 0; | |
/* count neighbouring cells */ | |
for (n = 0; n < sizeof moore / sizeof *moore; n++) { | |
unsigned nx = wrap(w, x + moore[n].x); | |
unsigned ny = wrap(h, y + moore[n].y); | |
count += *cell(nx, ny) & 1; | |
} | |
switch (count) { | |
case 0: | |
case 1: v = 0; break; | |
case 2: v = *cell(x, y); break; | |
case 3: v = 1; break; | |
default: v = 0; break; | |
} | |
*cell(x, y) |= (v << 1); | |
} | |
} | |
/* move the new generation over the previous */ | |
for (y = 0; y < h; y++) { | |
for (x = 0; x < w; x++) { | |
*cell(x, y) >>= 1; | |
} | |
} | |
} | |
static void | |
cleanup(void) | |
{ | |
free(board); | |
curs_set(cursor); | |
echo(); | |
} | |
int | |
main(int argc, char *argv[]) | |
{ | |
unsigned long g; | |
if (argc > 1) { | |
fprintf(stderr, "usage: life\n"); | |
return 1; | |
} | |
if (!setlocale(LC_CTYPE, "C.UTF-8")) { | |
perror("C.UTF-8"); | |
return 1; | |
} | |
initscr(); | |
w = (unsigned) COLS * 2; | |
h = (unsigned) LINES * 4; | |
noecho(); | |
cursor = curs_set(0); | |
atexit(cleanup); | |
#ifdef IMAGE_XBM | |
if (paste(IMAGE_NAME, _width) > w || paste(IMAGE_NAME, _height) > h) { | |
fprintf(stderr, "%s: Image dimensions must be %ux%u max\n", IMAGE_XBM, w, h); | |
exit(1); | |
} | |
#endif | |
board = calloc(w * h, 1); | |
if (board == NULL) { | |
perror("malloc"); | |
return 1; | |
} | |
/* to sleep for a short period of time in getch() */ | |
timeout(10); | |
cbreak(); | |
clear(); | |
fill(board); | |
for (g = 1; ; g++) { | |
print(board, g); | |
if (g == 1 && delay > DBL_EPSILON) { | |
usleep(delay); | |
} | |
if (decay > DBL_EPSILON && delay > DBL_EPSILON) { | |
delay *= decay; | |
usleep(delay); | |
} | |
if (step) { | |
getchar(); | |
} else if (getch() != ERR) { | |
return 0; | |
} | |
update(board); | |
if (persistence) { | |
fill(board); | |
} | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Just the XBM image conversion, without the Game of Life stuff. | |
* | |
* One way to make a suitable image: | |
* | |
* $ convert -scale x100 snorkmaiden.png /tmp/snorkmaiden.xbm | |
* | |
* I didn't write runtime image loading code. Images are given at compile time. | |
* XBM is very simple, and the source is an array in C code. | |
* Inside an .xbm file looks something like this, where the data is named | |
* after the filename (in this case snorkmaiden.xbm): | |
* | |
* #define snorkmaiden_width 57 | |
* #define snorkmaiden_height 61 | |
* static char snorkmaiden_bits[] = { 0xab, 0xcd, ... } | |
* | |
* To compile: | |
* | |
* $ gcc -DIMAGE_NAME=snorkmaiden -DIMAGE_XBM='"/tmp/snorkmaiden.xbm"' xbm-utf8.c | |
* | |
* Public domain. | |
* Katherine Flavel, @thingskatedid | |
*/ | |
#include <assert.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#define paste_(a, b) a##b | |
#define paste(a, b) paste_(a, b) | |
#ifndef IMAGE_NAME | |
#error Compile with -DIMAGE_NAME=xyz | |
#endif | |
#ifndef IMAGE_XBM | |
#error Compile with -DIMAGE_XBM='"/tmp/xyz.xbm"' | |
#endif | |
#ifdef IMAGE_XBM | |
#include IMAGE_XBM | |
#endif | |
static unsigned w; | |
static unsigned h; | |
static unsigned char *board; | |
/* Configurable things */ | |
static int invert; | |
static unsigned char * | |
cell(unsigned x, unsigned y) | |
{ | |
return &board[y * w + x]; | |
} | |
static void | |
utf8(unsigned cp, char c[]) | |
{ | |
if (cp <= 0x7f) { | |
c[0] = cp; | |
return; | |
} | |
if (cp <= 0x7ff) { | |
c[0] = (cp >> 6) + 192; | |
c[1] = (cp & 63) + 128; | |
return; | |
} | |
if (0xd800 <= cp && cp <= 0xdfff) { | |
/* invalid */ | |
goto error; | |
} | |
if (cp <= 0xffff) { | |
c[0] = (cp >> 12) + 224; | |
c[1] = ((cp >> 6) & 63) + 128; | |
c[2] = (cp & 63) + 128; | |
return; | |
} | |
if (cp <= 0x10ffff) { | |
c[0] = (cp >> 18) + 240; | |
c[1] = ((cp >> 12) & 63) + 128; | |
c[2] = ((cp >> 6) & 63) + 128; | |
c[3] = (cp & 63) + 128; | |
return; | |
} | |
error: | |
fprintf(stderr, "codepoint out of range\n"); | |
exit(1); | |
} | |
static void | |
print(const unsigned char *board) | |
{ | |
unsigned x; | |
unsigned y; | |
struct { | |
int x; | |
int y; | |
int m; | |
} braille[] = { | |
{ 0, 0, 0x2801 }, { 1, 0, 0x2808 }, | |
{ 0, 1, 0x2802 }, { 1, 1, 0x2810 }, | |
{ 0, 2, 0x2804 }, { 1, 2, 0x2820 }, | |
{ 0, 3, 0x2840 }, { 1, 3, 0x2880 } | |
}; | |
for (y = 0; y < h - 3; y += 4) { | |
for (x = 0; x < w - 1; x += 2) { | |
char s[5] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; | |
size_t i; | |
int cp; | |
cp = 0x2800; /* blank */ | |
for (i = 0; i < sizeof braille / sizeof *braille; i++) { | |
if (*cell(x + braille[i].x, y + braille[i].y)) { | |
cp |= braille[i].m; | |
} | |
} | |
utf8(cp, s); | |
fputs(s, stdout); | |
} | |
fputc('\n', stdout); | |
} | |
} | |
static int | |
xpm_pixel(unsigned x, unsigned y, unsigned width, unsigned height, const unsigned char *img) | |
{ | |
unsigned byte, bit; | |
/* xpm has eight bits per element. Rows are padded to a byte's width */ | |
width += 7; | |
width &= ~7; | |
byte = (y * width + x) / 8; | |
bit = (y * width + x) % 8; | |
return !!(img[byte] & (1U << bit)); | |
} | |
static void | |
fill_img(unsigned char *board, unsigned width, unsigned height, const void *img) | |
{ | |
unsigned x, y; | |
int pixel; | |
assert(img != NULL); | |
for (y = 0; y < height; y++) { | |
for (x = 0; x < width; x++) { | |
pixel = xpm_pixel(x, y, width, height, img); | |
if (invert) { | |
pixel = !pixel; | |
} | |
if (pixel) { | |
*cell(x, y) = pixel; | |
} | |
} | |
} | |
} | |
int | |
main(int argc, char *argv[]) | |
{ | |
unsigned long g; | |
if (argc > 1) { | |
fprintf(stderr, "usage: xbm\n"); | |
return 1; | |
} | |
w = paste(IMAGE_NAME, _width); | |
h = paste(IMAGE_NAME, _height); | |
board = calloc(w * h, 1); | |
if (board == NULL) { | |
perror("malloc"); | |
return 1; | |
} | |
fill_img(board, w, h, paste(IMAGE_NAME, _bits)); | |
print(board); | |
free(board); | |
return 0; | |
} |
Author
katef
commented
Jul 3, 2020
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment