vtv-from-ff.c - vtv-tools - virtual terminal video tools
 (HTM) git clone git://bitreich.org/vtv-tools  git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/vtv-tools
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
       vtv-from-ff.c (5425B)
       ---
            1 // Convert farbfeld image to vtv file.
            2 //
            3 // Can be run either in pipe mode or with file arguments.  A file
            4 // 'img.ff' is turned into a file 'img.vtv'.
            5 //
            6 // If you want to produce a vtv file that can be shown with
            7 // vtv-player, the image should be 25 lines by 73 columns.
            8 //
            9 // Copyright 2023 Troels Henriksen <athas@sigkill.dk>
           10 //
           11 // See LICENSE file for licensing information.
           12 
           13 #include <stdio.h>
           14 #include <string.h>
           15 #include <stdlib.h>
           16 #include <stdint.h>
           17 #include <errno.h>
           18 
           19 void def(FILE *f) {
           20   fprintf(f, "\033[0m");
           21 }
           22 
           23 void fg_rgb(FILE *f, uint8_t r, uint8_t g, uint8_t b) {
           24   fprintf(f, "\033[38;2;%d;%d;%dm", r, g, b);
           25 }
           26 
           27 void bg_rgb(FILE *f, uint8_t r, uint8_t g, uint8_t b) {
           28   fprintf(f, "\033[48;2;%d;%d;%dm", r, g, b);
           29 }
           30 
           31 int read_be_uint16(FILE *f, uint16_t *x) {
           32   uint8_t word[2];
           33 
           34   if (fread(&word, 1, 2, f) != 2) {
           35     return 1;
           36   }
           37   *x = (word[1] << 8) + word[0];
           38 
           39   return 0;
           40 }
           41 
           42 int read_be_uint32(FILE *f, uint32_t *x) {
           43   uint8_t word[4];
           44 
           45   if (fread(&word, 1, 4, f) != 4) {
           46     return 1;
           47   }
           48   *x = (word[0] << 24) + (word[1] << 16) + (word[2] << 8) + word[3];
           49 
           50   return 0;
           51 }
           52 
           53 int load_ff(FILE *f,
           54             uint16_t* *argbs_out,
           55             uint32_t *width_out,
           56             uint32_t *height_out) {
           57   char magic[8];
           58   uint16_t* argbs = NULL;
           59   uint32_t width = 0, height = 0;
           60 
           61   if (fread(magic, 1, 8, f) != 8) {
           62     goto bad;
           63   }
           64 
           65   if (memcmp(magic, "farbfeld", 8) != 0) {
           66     goto bad;
           67   }
           68 
           69   if (read_be_uint32(f, &width) != 0) {
           70     goto bad;
           71   }
           72 
           73   if (read_be_uint32(f, &height) != 0) {
           74     goto bad;
           75   }
           76 
           77   argbs = calloc(width*height*4, sizeof(uint16_t));
           78 
           79   for (unsigned int i = 0; i < width; i++) {
           80     for (unsigned int j = 0; j < height; j++) {
           81       for (unsigned int l = 0; l < 4; l++) {
           82         if (read_be_uint16(f, &argbs[i*(height*4)+(j*4)+l]) != 0) {
           83           goto bad;
           84         }
           85       }
           86     }
           87   }
           88 
           89   *argbs_out = argbs;
           90   *width_out = width;
           91   *height_out = height;
           92   return 0;
           93 
           94  bad:
           95   free(argbs);
           96   return 1;
           97 }
           98 
           99 void render(int nrows, int ncols, const uint16_t *argbs,
          100             uint32_t *fgs, uint32_t *bgs, char *chars) {
          101   for (int i = 0; i < nrows; i++) {
          102     for (int j = 0; j < ncols; j++) {
          103       uint32_t r0 = argbs[(i*2)*(ncols*4)+j*4+0]>>8;
          104       uint32_t g0 = argbs[(i*2)*(ncols*4)+j*4+1]>>8;
          105       uint32_t b0 = argbs[(i*2)*(ncols*4)+j*4+2]>>8;
          106       uint32_t r1 = argbs[(i*2+1)*(ncols*4)+j*4+0]>>8;
          107       uint32_t g1 = argbs[(i*2+1)*(ncols*4)+j*4+1]>>8;
          108       uint32_t b1 = argbs[(i*2+1)*(ncols*4)+j*4+2]>>8;
          109 
          110       uint32_t w0 = r0 << 16 | g0 << 8 | b0;
          111       uint32_t w1 = r1 << 16 | g1 << 8 | b1;
          112       fgs[i*ncols+j] = w0;
          113       bgs[i*ncols+j] = w1;
          114       chars[i*ncols+j] = 127; // Sentinel.
          115     }
          116   }
          117 }
          118 
          119 void display(FILE *f, int nrows, int ncols,
          120              const uint32_t *fgs, const uint32_t *bgs, const char *chars) {
          121   for (int i = 0; i < nrows; i++) {
          122     uint32_t prev_w0 = 0xdeadbeef;
          123     uint32_t prev_w1 = 0xdeadbeef;
          124     for (int j = 0; j < ncols; j++) {
          125       double r0 = 0, g0 = 0, b0 = 0;
          126       double r1 = 0, g1 = 0, b1 = 0;
          127       uint32_t w0 = fgs[i*ncols+j];
          128       uint32_t w1 = bgs[i*ncols+j];
          129       if (w0 != prev_w0 || w1 != prev_w1) {
          130         r0 = (w0>>16)&0xFF;
          131         g0 = (w0>>8)&0xFF;
          132         b0 = (w0>>0)&0xFF;
          133         r1 = (w1>>16)&0xFF;
          134         g1 = (w1>>8)&0xFF;
          135         b1 = (w1>>0)&0xFF;
          136         fg_rgb(f, r0, g0, b0);
          137         bg_rgb(f, r1, g1, b1);
          138         prev_w0 = w0;
          139         prev_w1 = w1;
          140       }
          141       char c = chars[i*ncols+j];
          142       if (c == 127) {
          143         fputs("▀", f);
          144       } else {
          145         fputc(c, f);
          146       }
          147     }
          148     def(f);
          149     fputc('\n', f);
          150   }
          151 }
          152 
          153 int convert(FILE *ff, FILE *vtv) {
          154   uint32_t width, height;
          155   uint16_t *argbs;
          156   if (load_ff(ff, &argbs, &width, &height) != 0) {
          157     return 1;
          158   }
          159   uint32_t *fgs = calloc(width*height/2, sizeof(uint32_t));
          160   uint32_t *bgs = calloc(width*height/2, sizeof(uint32_t));
          161   char *chars = calloc(width*height/2, sizeof(char));
          162   render(height/2, width, argbs, fgs, bgs, chars);
          163   display(vtv, height/2, width, fgs, bgs, chars);
          164   free(argbs);
          165   free(fgs);
          166   free(bgs);
          167   free(chars);
          168   return 0;
          169 }
          170 
          171 int main (int argc, char** argv) {
          172   if (argc == 1) {
          173     if (convert(stdin, stdout) != 0) {
          174       fprintf(stderr, "%s: invalid farbfeld image on stdin.\n", argv[0]);
          175       exit(1);
          176     }
          177   } else {
          178     for (int i = 1; i < argc; i++) {
          179       const char *ff_fname = argv[i];
          180       size_t len = strlen(ff_fname);
          181       if (len >= 3 && strcmp(&ff_fname[len-3], ".ff") == 0) {
          182         char *vtv_fname = malloc(len+2);
          183         strncpy(vtv_fname, ff_fname, len-3);
          184         strcpy(vtv_fname+len-3, ".vtv");
          185         printf("%s\n%s\n", ff_fname, vtv_fname);
          186         FILE *ff = fopen(ff_fname, "r");
          187         if (ff == NULL) {
          188           fprintf(stderr, "%s: could not open %s: %s\n",
          189                   argv[0], ff_fname, strerror(errno));
          190         }
          191         FILE *vtv = fopen(vtv_fname, "w+");
          192         if (vtv == NULL) {
          193           fprintf(stderr, "%s: could not open %s: %s\n",
          194                   argv[0], vtv_fname, strerror(errno));
          195         }
          196         if (convert(ff, vtv) != 0) {
          197           fprintf(stderr, "%s: invalid farbfeld image in %s.\n",
          198                   argv[0], ff_fname);
          199           exit(1);
          200         }
          201         fclose(ff);
          202         fclose(vtv);
          203         free(vtv_fname);
          204       } else {
          205         fprintf(stderr,
          206                 "%s: argument %s does not have .ff extension.\n",
          207                 argv[0], ff_fname);
          208         exit(1);
          209       }
          210     }
          211   }
          212 }