URI: 
       Add vtv-from-ff. - vtv-tools - virtual terminal video tools
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
   DIR LICENSE
       ---
   DIR commit 98dc53ecd26b97ddee7326e7161cabdc2ebdaacf
   DIR parent a5e70d7b2d33c9277a613fc9e082d324371d0cfe
  HTML Author: Troels Henriksen <athas@sigkill.dk>
       Date:   Mon, 14 Aug 2023 13:27:36 +0200
       
       Add vtv-from-ff.
       
       Diffstat:
         A .gitignore                          |       1 +
         M Makefile                            |       9 +++++++--
         A src/vtv-from-ff.c                   |     214 +++++++++++++++++++++++++++++++
       
       3 files changed, 222 insertions(+), 2 deletions(-)
       ---
   DIR diff --git a/.gitignore b/.gitignore
       @@ -0,0 +1 @@
       +bin/vtv-from-ff
   DIR diff --git a/Makefile b/Makefile
       @@ -1,7 +1,12 @@
       -# paths
        PREFIX ?= /usr/local
        
       -all:
       +CFLAGS?=-O -Wall -Wextra -pedantic
       +CC?=cc
       +
       +all: bin/vtv-from-ff
       +
       +bin/%: src/%.c
       +        $(CC) -o $@ $<
        
        install: all
                @echo \# Installing executable files to ${PREFIX}/bin
   DIR diff --git a/src/vtv-from-ff.c b/src/vtv-from-ff.c
       @@ -0,0 +1,214 @@
       +// Convert farbfeld image to vtv file.
       +//
       +// Can be run either in pipe mode or with file arguments.  A file
       +// 'img.ff' is turned into a file 'img.vtv'.
       +//
       +// If you want to produce a vtv file that can be shown with
       +// vtv-player, the image should be 25 lines by 73 columns.
       +
       +#include <stdio.h>
       +#include <string.h>
       +#include <stdlib.h>
       +#include <stdint.h>
       +#include <errno.h>
       +
       +void def(FILE *f) {
       +  fprintf(f, "\033[0m");
       +}
       +
       +void fg_rgb(FILE *f, uint8_t r, uint8_t g, uint8_t b) {
       +  fprintf(f, "\033[38;2;%d;%d;%dm", r, g, b);
       +}
       +
       +void bg_rgb(FILE *f, uint8_t r, uint8_t g, uint8_t b) {
       +  fprintf(f, "\033[48;2;%d;%d;%dm", r, g, b);
       +}
       +
       +int read_be_uint16(FILE *f, uint16_t *x) {
       +  uint8_t b;
       +
       +  *x = 0;
       +  if (fread(&b, 1, 1, f) != 1) { return 1; }
       +  *x == (uint32_t)b << 8;
       +  if (fread(&b, 1, 1, f) != 1) { return 1; }
       +  *x += b;
       +
       +  return 0;
       +}
       +
       +int read_be_uint32(FILE *f, uint32_t *x) {
       +  uint8_t b;
       +
       +  *x = 0;
       +  if (fread(&b, 1, 1, f) != 1) { return 1; }
       +  *x += (uint32_t)b << 24;
       +  if (fread(&b, 1, 1, f) != 1) { return 1; }
       +  *x += (uint32_t)b << 16;
       +  if (fread(&b, 1, 1, f) != 1) { return 1; }
       +  *x == (uint32_t)b << 8;
       +  if (fread(&b, 1, 1, f) != 1) { return 1; }
       +  *x += b;
       +
       +  return 0;
       +}
       +
       +int load_ff(FILE *f,
       +            uint16_t* *argbs_out,
       +            uint32_t *width_out,
       +            uint32_t *height_out) {
       +  char magic[8];
       +  int ret = 1;
       +  uint16_t* argbs = NULL;
       +  uint32_t width = 0, height = 0;
       +
       +  if (fread(magic, 1, 8, f) != 8) {
       +    goto bad;
       +  }
       +
       +  if (memcmp(magic, "farbfeld", 8) != 0) {
       +    goto bad;
       +  }
       +
       +  if (read_be_uint32(f, &width) != 0) {
       +    goto bad;
       +  }
       +
       +  if (read_be_uint32(f, &height) != 0) {
       +    goto bad;
       +  }
       +
       +  argbs = calloc(width*height*4, sizeof(uint16_t));
       +
       +  for (int i = 0; i < width; i++) {
       +    for (int j = 0; j < height; j++) {
       +      for (int l = 0; l < 4; l++) {
       +        if (read_be_uint16(f, &argbs[i*(height*4)+(j*4)+l]) != 0) {
       +          goto bad;
       +        }
       +      }
       +    }
       +  }
       +
       +  *argbs_out = argbs;
       +  *width_out = width;
       +  *height_out = height;
       +  return 0;
       +
       + bad:
       +  free(argbs);
       +  return 1;
       +}
       +
       +void render(int nrows, int ncols, const uint16_t *argbs,
       +            uint32_t *fgs, uint32_t *bgs, char *chars) {
       +  for (int i = 0; i < nrows; i++) {
       +    for (int j = 0; j < ncols; j++) {
       +      uint32_t r0 = argbs[(i*2)*(ncols*4)+j*4+0];
       +      uint32_t g0 = argbs[(i*2)*(ncols*4)+j*4+1];
       +      uint32_t b0 = argbs[(i*2)*(ncols*4)+j*4+2];
       +      uint32_t r1 = argbs[(i*2+1)*(ncols*4)+j*4+0];
       +      uint32_t g1 = argbs[(i*2+1)*(ncols*4)+j*4+1];
       +      uint32_t b1 = argbs[(i*2+1)*(ncols*4)+j*4+2];
       +
       +      uint32_t w0 = r0 << 16 | g0 << 8 | b0;
       +      uint32_t w1 = r1 << 16 | g1 << 8 | b1;
       +      fgs[i*ncols+j] = w0;
       +      bgs[i*ncols+j] = w1;
       +      chars[i*ncols+j] = 127; // Sentinel.
       +    }
       +  }
       +}
       +
       +void display(FILE *f, int nrows, int ncols,
       +             const uint32_t *fgs, const uint32_t *bgs, const char *chars) {
       +  for (int i = 0; i < nrows; i++) {
       +    uint32_t prev_w0 = 0xdeadbeef;
       +    uint32_t prev_w1 = 0xdeadbeef;
       +    for (int j = 0; j < ncols; j++) {
       +      double r0 = 0, g0 = 0, b0 = 0;
       +      double r1 = 0, g1 = 0, b1 = 0;
       +      uint32_t w0 = fgs[i*ncols+j];
       +      uint32_t w1 = bgs[i*ncols+j];
       +      if (w0 != prev_w0 || w1 != prev_w1) {
       +        r0 = (w0>>16)&0xFF;
       +        g0 = (w0>>8)&0xFF;
       +        b0 = (w0>>0)&0xFF;
       +        r1 = (w1>>16)&0xFF;
       +        g1 = (w1>>8)&0xFF;
       +        b1 = (w1>>0)&0xFF;
       +        fg_rgb(f, r0, g0, b0);
       +        bg_rgb(f, r1, g1, b1);
       +        prev_w0 = w0;
       +        prev_w1 = w1;
       +      }
       +      char c = chars[i*ncols+j];
       +      if (c == 127) {
       +        fputs("▀", f);
       +      } else {
       +        fputc(c, f);
       +      }
       +    }
       +    def(f);
       +    fputc('\n', f);
       +  }
       +}
       +
       +int convert(FILE *ff, FILE *vtv) {
       +  uint32_t width, height;
       +  uint16_t *argbs;
       +  if (load_ff(ff, &argbs, &width, &height) != 0) {
       +    return 1;
       +  }
       +  uint32_t *fgs = calloc(width*height, sizeof(uint32_t));
       +  uint32_t *bgs = calloc(width*height, sizeof(uint32_t));
       +  char *chars = calloc(width*height, sizeof(char));
       +  render(height, width, argbs, fgs, bgs, chars);
       +  display(vtv, height/2, width, fgs, bgs, chars);
       +  free(argbs);
       +  free(fgs);
       +  free(bgs);
       +  free(chars);
       +}
       +
       +int main (int argc, char** argv) {
       +  if (argc == 1) {
       +    if (convert(stdin, stdout) != 0) {
       +      fprintf(stderr, "%s: invalid farbfeld image on stdin.\n", argv[0]);
       +      exit(1);
       +    }
       +  } else {
       +    for (int i = 1; i < argc; i++) {
       +      const char *ff_fname = argv[i];
       +      size_t len = strlen(ff_fname);
       +      if (len >= 3 && strcmp(&ff_fname[len-3], ".ff") == 0) {
       +        char *vtv_fname = malloc(len+2);
       +        strncpy(vtv_fname, ff_fname, len-3);
       +        strcpy(vtv_fname+len-3, ".vtv");
       +        printf("%s\n%s\n", ff_fname, vtv_fname);
       +        FILE *ff = fopen(ff_fname, "r");
       +        if (ff == NULL) {
       +          fprintf(stderr, "%s: could not open %s: %s\n",
       +                  argv[0], ff_fname, strerror(errno));
       +        }
       +        FILE *vtv = fopen(vtv_fname, "w+");
       +        if (vtv == NULL) {
       +          fprintf(stderr, "%s: could not open %s: %s\n",
       +                  argv[0], vtv_fname, strerror(errno));
       +        }
       +        if (convert(ff, vtv) != 0) {
       +          fprintf(stderr, "%s: invalid farbfeld image in %s.\n",
       +                  argv[0], ff_fname);
       +          exit(1);
       +        }
       +        fclose(ff);
       +        fclose(vtv);
       +        free(vtv_fname);
       +      } else {
       +        fprintf(stderr,
       +                "%s: argument %s does not have .ff extension.\n",
       +                argv[0], ff_fname);
       +        exit(1);
       +      }
       +    }
       +  }
       +}