add farbfeld output

This commit is contained in:
Kiëd Llaentenn 2020-09-19 12:59:55 +00:00
parent fc0d0207aa
commit 6765bb8fc7
11 changed files with 334 additions and 90 deletions

8
args.h
View File

@ -26,11 +26,17 @@ extern char *argv0;
typedef struct Options { typedef struct Options {
size_t refresh_rate; size_t refresh_rate;
size_t animation_speed; size_t animation_speed;
size_t max_heat_loss; size_t heat_loss;
size_t wind; size_t wind;
size_t random_factor; size_t random_factor;
bool random_wind; bool random_wind;
bool truecolor; bool truecolor;
uint32_t width;
uint32_t height;
bool farbfeld_mode;
size_t ff_frame;
} Options; } Options;
/* use main(int argc, char *argv[]) */ /* use main(int argc, char *argv[]) */

70
colors.c Normal file
View File

@ -0,0 +1,70 @@
#ifndef COLORS_INCLUDED
#define COLORS_INCLUDED
#include "colors.h"
#include "termbox.h"
#define BLACK 0x000000
const struct tb_cell normcolors[13] = {
// default
{ ' ', 9, 0 },
// red/black
{ 0x2591, 2, 0 },
{ 0x2592, 2, 0 },
{ 0x2593, 2, 0 },
{ 0x2588, 2, 0 },
// yellow/red
{ 0x2591, 4, 2 },
{ 0x2592, 4, 2 },
{ 0x2593, 4, 2 },
{ 0x2588, 4, 2 },
// white/red
{ 0x2591, 8, 2 },
{ 0x2592, 8, 2 },
{ 0x2593, 8, 2 },
{ 0x2588, 8, 2 },
};
const struct tb_cell truecolors[36] = {
{ ' ', BLACK, 0x070707 },
{ ' ', BLACK, 0x1F0707 },
{ ' ', BLACK, 0x2F0F07 },
{ ' ', BLACK, 0x470F07 },
{ ' ', BLACK, 0x571707 },
{ ' ', BLACK, 0x671F07 },
{ ' ', BLACK, 0x771F07 },
{ ' ', BLACK, 0x8F2707 },
{ ' ', BLACK, 0x9F2F07 },
{ ' ', BLACK, 0xAF3F07 },
{ ' ', BLACK, 0xBF4707 },
{ ' ', BLACK, 0xC74707 },
{ ' ', BLACK, 0xDF4F07 },
{ ' ', BLACK, 0xDF5707 },
{ ' ', BLACK, 0xDF5707 },
{ ' ', BLACK, 0xD75F07 },
{ ' ', BLACK, 0xD7670F },
{ ' ', BLACK, 0xCF6F0F },
{ ' ', BLACK, 0xCF770F },
{ ' ', BLACK, 0xCF7F0F },
{ ' ', BLACK, 0xCF8717 },
{ ' ', BLACK, 0xC78717 },
{ ' ', BLACK, 0xC78F17 },
{ ' ', BLACK, 0xC7971F },
{ ' ', BLACK, 0xBF9F1F },
{ ' ', BLACK, 0xBF9F1F },
{ ' ', BLACK, 0xBFA727 },
{ ' ', BLACK, 0xBFA727 },
{ ' ', BLACK, 0xBFAF2F },
{ ' ', BLACK, 0xB7AF2F },
{ ' ', BLACK, 0xB7B737 },
{ ' ', BLACK, 0xCFCF6F },
{ ' ', BLACK, 0xDFDF9F },
{ ' ', BLACK, 0xEFEFC7 },
{ ' ', BLACK, 0xFFFFFF }
};
#endif

View File

@ -3,74 +3,7 @@
#include "termbox.h" #include "termbox.h"
#ifdef __OpenBSD__ extern struct tb_cell normcolors[13];
#include "sys/types.h" extern struct tb_cell truecolors[36];
#else
#include "stdint.h"
#endif
#define BLACK 0x000000
struct tb_cell normcolors[13] =
{
// default
{ ' ', 9, 0 },
// red/black
{ 0x2591, 2, 0 },
{ 0x2592, 2, 0 },
{ 0x2593, 2, 0 },
{ 0x2588, 2, 0 },
// yellow/red
{ 0x2591, 4, 2 },
{ 0x2592, 4, 2 },
{ 0x2593, 4, 2 },
{ 0x2588, 4, 2 },
// white/red
{ 0x2591, 8, 2 },
{ 0x2592, 8, 2 },
{ 0x2593, 8, 2 },
{ 0x2588, 8, 2 },
};
struct tb_cell truecolors[36] = {
{ ' ', BLACK, 0x070707 },
{ ' ', BLACK, 0x1F0707 },
{ ' ', BLACK, 0x2F0F07 },
{ ' ', BLACK, 0x470F07 },
{ ' ', BLACK, 0x571707 },
{ ' ', BLACK, 0x671F07 },
{ ' ', BLACK, 0x771F07 },
{ ' ', BLACK, 0x8F2707 },
{ ' ', BLACK, 0x9F2F07 },
{ ' ', BLACK, 0xAF3F07 },
{ ' ', BLACK, 0xBF4707 },
{ ' ', BLACK, 0xC74707 },
{ ' ', BLACK, 0xDF4F07 },
{ ' ', BLACK, 0xDF5707 },
{ ' ', BLACK, 0xDF5707 },
{ ' ', BLACK, 0xD75F07 },
{ ' ', BLACK, 0xD7670F },
{ ' ', BLACK, 0xCF6F0F },
{ ' ', BLACK, 0xCF770F },
{ ' ', BLACK, 0xCF7F0F },
{ ' ', BLACK, 0xCF8717 },
{ ' ', BLACK, 0xC78717 },
{ ' ', BLACK, 0xC78F17 },
{ ' ', BLACK, 0xC7971F },
{ ' ', BLACK, 0xBF9F1F },
{ ' ', BLACK, 0xBF9F1F },
{ ' ', BLACK, 0xBFA727 },
{ ' ', BLACK, 0xBFA727 },
{ ' ', BLACK, 0xBFAF2F },
{ ' ', BLACK, 0xB7AF2F },
{ ' ', BLACK, 0xB7B737 },
{ ' ', BLACK, 0xCFCF6F },
{ ' ', BLACK, 0xDFDF9F },
{ ' ', BLACK, 0xEFEFC7 },
{ ' ', BLACK, 0xFFFFFF }
};
#endif #endif

32
draw.c
View File

@ -22,11 +22,11 @@ extern struct Options *opts;
// initialize the framebuffer // initialize the framebuffer
void void
init(struct buffer *buf) init(struct buffer *buf, uint16_t width, uint16_t height)
{ {
// initialize width/height of terminal // initialize width/height of terminal
buf->width = tb_width(); buf->width = width;
buf->height = tb_height(); buf->height = height;
size_t len = buf->width * buf->height; size_t len = buf->width * buf->height;
buf->buf = (uint8_t*) calloc(len, sizeof(uint8_t)); buf->buf = (uint8_t*) calloc(len, sizeof(uint8_t));
@ -49,21 +49,26 @@ void
dofire(struct buffer *buf) dofire(struct buffer *buf)
{ {
size_t src; size_t src;
size_t random = (rand() % 7) & 3; size_t rnd_wind = (lrand48() % 7) & 3;
size_t rnd_lose = lrand48() % 100;
size_t rnd_loss = (lrand48() % 7) & 3;
size_t dest; size_t dest;
struct tb_cell *realbuf = tb_cell_buffer(); struct tb_cell *realbuf = tb_cell_buffer();
for (size_t x = 0; x < buf->width; ++x) { for (size_t x = 0; x < buf->width; ++x) {
for (size_t y = 1; y < buf->height; ++y) { for (size_t y = 1; y < buf->height; ++y) {
if ((rand() % opts->random_factor) == 0) { // TODO; test rngs
random = (rand() % 7) & 3; if ((lrand48() % opts->random_factor) == 0) {
rnd_wind = (lrand48() % 7) & 3;
rnd_lose = lrand48() % 100;
rnd_loss = (lrand48() % 7) & 3;
} }
src = y * buf->width + x; src = y * buf->width + x;
if (opts->random_wind) { if (opts->random_wind) {
dest = src - random + opts->wind; dest = src - rnd_wind + opts->wind;
} else { } else {
dest = src + opts->wind; dest = src + opts->wind;
} }
@ -84,13 +89,21 @@ dofire(struct buffer *buf)
dest -= buf->width; dest -= buf->width;
} }
size_t loss = MIN(opts->max_heat_loss, 3); size_t loss = rnd_lose < opts->heat_loss ? 2 : 0;
buf->buf[dest] = MAX(buf->buf[src] - (random & loss), 0); buf->buf[dest] = MAX(buf->buf[src] - (rnd_loss & loss), 0);
if (buf->buf[dest] > max_value) { if (buf->buf[dest] > max_value) {
buf->buf[dest] = 0; buf->buf[dest] = 0;
} }
// TODO: comment everything
// copy from our buffer to termbox's buffer
// unless, of course, our buffer is bigger
if (src >= tb_width() * tb_height()) {
continue;
}
realbuf[dest] = colors[buf->buf[dest]]; realbuf[dest] = colors[buf->buf[dest]];
realbuf[src] = colors[buf->buf[src]]; realbuf[src] = colors[buf->buf[src]];
} }
@ -103,5 +116,4 @@ void
cleanup(struct buffer *buf) cleanup(struct buffer *buf)
{ {
free(buf->buf); free(buf->buf);
tb_shutdown();
} }

2
draw.h
View File

@ -14,7 +14,7 @@ struct buffer {
uint8_t* buf; uint8_t* buf;
}; };
void init(struct buffer *buf); void init(struct buffer *buf, uint16_t width, uint16_t height);
void dofire(struct buffer *buf); void dofire(struct buffer *buf);
void cleanup(struct buffer *buf); void cleanup(struct buffer *buf);

54
ff.c Normal file
View File

@ -0,0 +1,54 @@
/*
* winsock.h on windows,
* arpa/inet.h on unix
* for htons()
*/
#if defined(_WIN32) || defined(__WIN32__)
#include <winsock.h>
#else
#include <arpa/inet.h>
#endif
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "termbox.h"
#include "bool.h"
#include "ff.h"
static uint16_t
clr(uint32_t clr)
{
uint16_t nclr = (((uint16_t) clr) & 0xff) & 0xffff;
return nclr | (nclr << 8);
}
void
ff_from_tbscr(uint32_t width, uint32_t height, uint8_t *img, struct tb_cell *colors, FILE *fp)
{
/* write farbfeld headers */
fputs("farbfeld", fp);
uint32_t tmp;
tmp = htonl(width);
fwrite(&tmp, sizeof(tmp), 1, fp);
tmp = htonl(height);
fwrite(&tmp, sizeof(tmp), 1, fp);
/* write image row by row */
uint16_t *rowbuf = malloc(width * (4 * sizeof(uint16_t)));
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
uint32_t color = colors[img[y * width + x]].bg;
rowbuf[4 * x + 0] = htons(clr(color >> 16));
rowbuf[4 * x + 1] = htons(clr(color >> 8));
rowbuf[4 * x + 2] = htons(clr(color));
rowbuf[4 * x + 3] = htons((uint16_t) 0xffff);
}
fwrite(rowbuf, sizeof(uint16_t), width * 4, fp);
memset(rowbuf, 0, width * (4 * sizeof(uint16_t)));
}
}

15
ff.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef FF_H
#define FF_H
#include <stdio.h>
#include <stdint.h>
#include "termbox.h"
#include "bool.h"
void ff_from_tbscr(
uint32_t width, uint32_t height,
uint8_t *img, struct tb_cell *colors,
FILE *fp
);
#endif

53
main.c
View File

@ -1,10 +1,16 @@
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "bool.h" #include "bool.h"
#include "output.h" #include "output.h"
#include "draw.h" #include "draw.h"
#include "colors.h"
#include "terminfo.h"
#include "termbox.h" #include "termbox.h"
#include "args.h" #include "args.h"
#include "ff.h"
#ifdef __OpenBSD__ #ifdef __OpenBSD__
#include "sys/types.h" #include "sys/types.h"
@ -31,15 +37,29 @@ main(int argc, char *argv[])
opts->refresh_rate = 1; opts->refresh_rate = 1;
size_t output_mode = TB_OUTPUT_NORMAL; size_t output_mode = TB_OUTPUT_NORMAL;
opts->truecolor = FALSE; opts->truecolor = FALSE;
opts->max_heat_loss = 1; opts->heat_loss = 45;
opts->wind = 1; opts->wind = 1;
opts->random_wind = TRUE; opts->random_wind = TRUE;
opts->random_factor = 4; opts->random_factor = 4;
opts->width = ttywidth();
opts->height = ttyheight();
// argument parsing // argument parsing
argv0 = argv[0]; argv0 = argv[0];
ARGBEGIN { ARGBEGIN {
case 'F':
opts->farbfeld_mode = TRUE;
opts->ff_frame = atoi(ARGF());
output_mode = TB_OUTPUT_TRUECOLOR;
opts->truecolor = TRUE;
break;
case 'H':
opts->height = atoi(ARGF());
break;
case 'W':
opts->width = atoi(ARGF());
break;
case 't': case 't':
output_mode = TB_OUTPUT_TRUECOLOR; output_mode = TB_OUTPUT_TRUECOLOR;
opts->truecolor = TRUE; opts->truecolor = TRUE;
@ -51,7 +71,7 @@ main(int argc, char *argv[])
opts->refresh_rate = atoi(ARGF()); opts->refresh_rate = atoi(ARGF());
break; break;
case 'l': case 'l':
opts->max_heat_loss = atoi(ARGF()); opts->heat_loss = atoi(ARGF());
break; break;
case 'w': case 'w':
opts->wind = atoi(ARGF()); opts->wind = atoi(ARGF());
@ -62,6 +82,7 @@ main(int argc, char *argv[])
case 'f': case 'f':
opts->random_factor = atoi(ARGF()); opts->random_factor = atoi(ARGF());
break; break;
case 'v':
case 'V': case 'V':
printf("%s %s\n", argv0, VERSION); printf("%s %s\n", argv0, VERSION);
return 0; return 0;
@ -74,7 +95,7 @@ main(int argc, char *argv[])
printf(" Higher values == slower refresh rate.\n"); printf(" Higher values == slower refresh rate.\n");
printf(" -s [speed] Change animation speed. (default: 5)\n"); printf(" -s [speed] Change animation speed. (default: 5)\n");
printf(" Higher values == slower animation.\n"); printf(" Higher values == slower animation.\n");
printf(" -l [loss] Maximum heat loss for each row upward. (default: 1)\n"); printf(" -l [loss] Heat loss for each row upward. (default: 45)\n");
printf(" Higher values will lead to a smaller fire.\n"); printf(" Higher values will lead to a smaller fire.\n");
printf(" -w [wind] Wind. Negative values, or values less than one will\n"); printf(" -w [wind] Wind. Negative values, or values less than one will\n");
printf(" cause the fire to be blown west. (default: 1)\n"); printf(" cause the fire to be blown west. (default: 1)\n");
@ -99,14 +120,15 @@ main(int argc, char *argv[])
} ARGEND } ARGEND
// initialize termbox // initialize termbox
tb_init(); tb_init(); /* check return value */
tb_select_output_mode(output_mode); tb_select_output_mode(output_mode);
tb_clear(); tb_clear();
struct buffer buf; struct buffer buf;
struct tb_event e; struct tb_event e;
// initialize drawing // initialize drawing
init(&buf); init(&buf, opts->width, opts->height);
uint64_t ctr = 0; uint64_t ctr = 0;
@ -117,7 +139,24 @@ main(int argc, char *argv[])
// update framebuffer // update framebuffer
dofire(&buf); dofire(&buf);
if ((ctr % opts->refresh_rate) != 0) { /* check if we should print the image now */
if (opts->farbfeld_mode && ctr >= opts->ff_frame) {
tb_shutdown(); /* shutdown so we can output the image to stdout */
if (isatty(STDOUT_FILENO)) {
EPRINT("fire: cowardly refusing to write farbfeld image to the terminal.\n");
return 1;
}
ff_from_tbscr(opts->width, opts->height, buf.buf,
(struct tb_cell*) &truecolors, stdout);
cleanup(&buf);
return 0;
}
/* don't display or check for events if not on refresh frame */
if ((ctr % opts->refresh_rate) != 0 || opts->farbfeld_mode) {
continue; continue;
} }
@ -133,6 +172,7 @@ main(int argc, char *argv[])
// handle keypresses // handle keypresses
// q, ctrl+c, ctrl+d => quit // q, ctrl+c, ctrl+d => quit
/* TODO: handle resizes */
if (e.type == TB_EVENT_KEY) { if (e.type == TB_EVENT_KEY) {
switch (e.ch) { switch (e.ch) {
case 'q': case 'q':
@ -153,5 +193,6 @@ main(int argc, char *argv[])
cleanup: cleanup:
cleanup(&buf); cleanup(&buf);
tb_shutdown();
return 0; return 0;
} }

View File

@ -11,11 +11,11 @@ WARNING = -Wall -Wextra -pedantic -Wmissing-prototypes \
INC = -Isub/termbox_next/src INC = -Isub/termbox_next/src
CC = gcc CC = gcc
CFLAGS = -std=c99 $(WARNING) $(INC) -fsanitize=address -Og -ggdb CFLAGS = -std=c99 -O0 -ggdb $(WARNING) $(INC) #-fsanitize=address
LDFLAGS = LDFLAGS = -static
TRMBOX = sub/termbox_next/bin/termbox.a TRMBOX = sub/termbox_next/bin/termbox.a
SRC = main.c draw.c SRC = main.c draw.c ff.c colors.c terminfo.c
OBJ = $(SRC:.c=.o) OBJ = $(SRC:.c=.o)
DESTDIR = / DESTDIR = /

95
terminfo.c Normal file
View File

@ -0,0 +1,95 @@
/*
* terminfo.c: get TTY/console's buffer
* height and width.
*
* TODO: test this on Windows 10.
*
* why is all Windows programming so
* damn complicated.
*
* Thanks to this SO question for Windows stuff:
* stackoverflow.com/questions/6812224/getting-terminal-size-in-c-for-windows
*
* (c) Kiëd Llaentenn and contributors
* See the LICENSE.md file for copyright
* information.
*/
#include <stdio.h>
#include <stdint.h>
#include "terminfo.h"
#if defined(_WIN32) || defined(__WIN32__)
#define WOE_IS_ME
#endif
#ifdef WOE_IS_ME
#include <windows.h>
#else
#include <sys/ioctl.h>
#include <unistd.h>
#endif
/*
* sizes to fall back to if:
* 1) stdout isn't a tty
* 2) if on Windows (temporary, only until win32
* support it added
*/
const uint16_t fallback_width = 80;
const uint16_t fallback_height = 24;
/* file descriptor to get dimensions of */
#ifdef WOE_IS_ME
CONST DWORD fd = STD_OUTPUT_HANDLE;
#else
const size_t fd = STDOUT_FILENO;
#endif
uint16_t
ttywidth(void)
{
#ifdef WOE_IS_ME
/* why on earth does _isatty return NONZERO
* if FD is a tty?! */
if (_isatty(fd))
return fallback_width;
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(
GetStdHandle(fd), &csbi
);
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
#else
if (!isatty(fd))
return fallback_width;
struct winsize w;
ioctl(fd, TIOCGWINSZ, &w);
return w.ws_col;
#endif
}
uint16_t
ttyheight(void)
{
#ifdef WOE_IS_ME
if (_isatty(fd))
return fallback_height;
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(
GetStdHandle(fd), &csbi
);
return csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
#else
if (!isatty(fd))
return fallback_height;
struct winsize w;
ioctl(fd, TIOCGWINSZ, &w);
return w.ws_row;
#endif
}

18
terminfo.h Normal file
View File

@ -0,0 +1,18 @@
/*
* terminfo.h: get TTY/console's buffer
* height and width.
*
* (c) Kiëd Llaentenn and contributors
* See the LICENSE.md file for copyright
* information.
*/
#ifndef TERMINFO_H
#define TERMINFO_H
#include <stdint.h>
uint16_t ttywidth(void);
uint16_t ttyheight(void);
#endif