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 }