|
|
vtv-from-ff.c - vtv-tools - virtual terminal video tools |
|
|
 |
git clone git://bitreich.org/vtv-tools git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/vtv-tools (git://bitreich.org) |
|
|
 |
Log |
|
|
 |
Files |
|
|
 |
Refs |
|
|
 |
Tags |
|
|
 |
README |
|
|
 |
LICENSE |
|
|
|
--- |
|
|
|
vtv-from-ff.c (5417B) |
|
|
|
--- |
|
|
|
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, sizeof(uint32_t)); |
|
|
|
160 uint32_t *bgs = calloc(width*height, sizeof(uint32_t)); |
|
|
|
161 char *chars = calloc(width*height, sizeof(char)); |
|
|
|
162 render(height, 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 } |
|