blind-convert.c - blind - suckless command-line video editing utility
(HTM) git clone git://git.suckless.org/blind
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
blind-convert.c (12962B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include "common.h"
3
4 USAGE("pixel-format ...")
5
6 static int in_level = INT_MAX;
7 static int out_level = INT_MAX;
8
9 static void
10 lf_to_f(double *in, float *out, size_t n)
11 {
12 size_t i;
13 for (i = 0; i < n; i++)
14 out[i] = (float)(in[i]);
15 }
16
17 static void
18 f_to_lf(double *in, float *out, size_t n)
19 {
20 size_t i;
21 for (i = 0; i < n; i++)
22 out[i] = (double)(in[i]);
23 }
24
25 #if !defined(HOST_ENDIAN_IS_LITTLE_ENDIAN_16)
26 static void
27 le_to_h_16(uint16_t *buf, size_t n)
28 {
29 size_t i;
30 for (i = 0; i < n; i++)
31 buf[i] = letoh(buf[i]);
32 }
33
34 static void
35 h_to_le_16(uint16_t *buf, size_t n)
36 {
37 size_t i;
38 for (i = 0; i < n; i++)
39 buf[i] = htole(buf[i]);
40 }
41 #else
42 # define le_to_h_16(buf, n) ((void) buf, (void) n)
43 # define h_to_le_16(buf, n) ((void) buf, (void) n)
44 #endif
45
46 static size_t
47 remove_alpha_u16(uint16_t *buf, size_t n)
48 {
49 size_t i, j;
50 long int a, max = (long int)0xFF00L, ymax = 0xDAF4L;
51 for (i = j = 0; i < n; i += 4, j += 3) {
52 a = (long int)(buf[i + 3]);
53 buf[j + 0] = (uint16_t)(((long int)(buf[i + 0]) - 0x1001L) * a / ymax + 0x1001L);
54 buf[j + 1] = (uint16_t)(((long int)(buf[i + 1]) - 0x8000L) * a / max + 0x8000L);
55 buf[j + 2] = (uint16_t)(((long int)(buf[i + 2]) - 0x8000L) * a / max + 0x8000L);
56 }
57 return j;
58 }
59
60 #define REMOVE_ALPHA()\
61 do {\
62 size_t i, j;\
63 for (i = j = 0; i < n; i += 4, j += 3) {\
64 buf[j + 0] = buf[i + 0] * buf[i + 3];\
65 buf[j + 1] = buf[i + 1] * buf[i + 3];\
66 buf[j + 2] = buf[i + 2] * buf[i + 3];\
67 }\
68 return j;\
69 } while (0)
70
71 static size_t remove_alpha_lf(double *buf, size_t n) { REMOVE_ALPHA(); }
72 static size_t remove_alpha_f (float *buf, size_t n) { REMOVE_ALPHA(); }
73
74 #define ADD_ALPHA(TYPE, MAX)\
75 do {\
76 size_t i = n, j = n + n / 3;\
77 for (; i; i -= 3, j -= 4) {\
78 out[j - 1] = (TYPE)(MAX);\
79 out[j - 2] = in[i - 1];\
80 out[j - 3] = in[i - 2];\
81 out[j - 4] = in[i - 3];\
82 }\
83 return n + n / 3;\
84 } while (0)
85
86 static size_t add_alpha_u16(uint16_t *in, uint16_t *out, size_t n) { ADD_ALPHA(uint16_t, UINT16_MAX); }
87 static size_t add_alpha_lf (double *in, double *out, size_t n) { ADD_ALPHA(double, 1); }
88 static size_t add_alpha_f (float *in, float *out, size_t n) { ADD_ALPHA(float, 1); }
89
90 static void
91 raw0_to_raw1(uint16_t *buf, size_t n)
92 {
93 size_t i;
94 uint16_t t;
95 for (i = 0; i < n; i += 4, buf += 4)
96 t = buf[0], buf[0] = buf[1], buf[1] = buf[2], buf[2] = buf[3], buf[3] = t;
97 }
98
99 static void
100 raw1_to_raw0(uint16_t *buf, size_t n)
101 {
102 size_t i;
103 uint16_t t;
104 for (i = 0; i < n; i += 4, buf += 4)
105 t = buf[3], buf[3] = buf[2], buf[2] = buf[1], buf[1] = buf[0], buf[0] = t;
106 }
107
108 #define RAW2_TO_RAW3(TYPE, WITH_ALPHA)\
109 do {\
110 size_t i;\
111 TYPE max = (TYPE)0xFF00L, ymax = (TYPE)0xDAF4L;\
112 if (sizeof(*in) > sizeof(*out)) {\
113 for (i = 0; i < n; i += 3 + WITH_ALPHA) {\
114 out[i + 0] = (TYPE)((long int)(in[i + 0]) - 0x1001L) / ymax;\
115 out[i + 1] = (TYPE)((long int)(in[i + 1]) - 0x8000L) / max;\
116 out[i + 2] = (TYPE)((long int)(in[i + 2]) - 0x8000L) / max;\
117 if (WITH_ALPHA) {\
118 out[i + 3] = (TYPE)(in[i + 3]) / max;\
119 out[i + 3] = CLIP(0, out[i + 3], 1);\
120 }\
121 }\
122 } else {\
123 for (i = n; i; i -= 3 + WITH_ALPHA) {\
124 if (WITH_ALPHA) {\
125 out[i - 1] = (TYPE)(in[i - 1]) / max;\
126 out[i - 1] = CLIP(0, out[i - 1], 1);\
127 }\
128 out[i - 1 - WITH_ALPHA] = (TYPE)((long int)(in[i - 1 - WITH_ALPHA]) - 0x8000L) / max;\
129 out[i - 2 - WITH_ALPHA] = (TYPE)((long int)(in[i - 2 - WITH_ALPHA]) - 0x8000L) / max;\
130 out[i - 3 - WITH_ALPHA] = (TYPE)((long int)(in[i - 3 - WITH_ALPHA]) - 0x1001L) / ymax;\
131 }\
132 }\
133 } while (0)
134
135 static void raw2_to_raw3_lf (uint16_t *in, double *out, size_t n) { RAW2_TO_RAW3(double, 0); }
136 static void raw2_to_raw3_f (uint16_t *in, float *out, size_t n) { RAW2_TO_RAW3(float, 0); }
137 static void raw2a_to_raw3a_lf(uint16_t *in, double *out, size_t n) { RAW2_TO_RAW3(double, 1); }
138 static void raw2a_to_raw3a_f (uint16_t *in, float *out, size_t n) { RAW2_TO_RAW3(float, 1); }
139
140 #define RAW3_TO_RAW2(TYPE, WITH_ALPHA)\
141 do {\
142 size_t i;\
143 TYPE max = (TYPE)0xFF00L, ymax = (TYPE)0xDAF4L;\
144 long int y, u, v;\
145 if (sizeof(*in) > sizeof(*out)) {\
146 for (i = 0; i < n; i += 3 + WITH_ALPHA) {\
147 y = (long int)(in[i + 0] * ymax) + 0x1001L;\
148 u = (long int)(in[i + 1] * max) + 0x8000L;\
149 v = (long int)(in[i + 2] * max) + 0x8000L;\
150 out[i + 0] = (uint16_t)CLIP(0, y, 0xFFFFL);\
151 out[i + 1] = (uint16_t)CLIP(0, u, 0xFFFFL);\
152 out[i + 2] = (uint16_t)CLIP(0, v, 0xFFFFL);\
153 if (WITH_ALPHA) {\
154 v = (long int)(in[i + 3] * max);\
155 out[i + 3] = (uint16_t)CLIP(0, v, 0xFFFFL);\
156 }\
157 }\
158 } else {\
159 for (i = n; i; i -= 3 + WITH_ALPHA) {\
160 if (WITH_ALPHA) {\
161 v = (long int)(in[i - 1] * max);\
162 out[i - 1] = (uint16_t)CLIP(0, v, 0xFFFFL); \
163 }\
164 v = (long int)(in[i - 1 - WITH_ALPHA] * max) + 0x8000L;\
165 u = (long int)(in[i - 2 - WITH_ALPHA] * max) + 0x8000L;\
166 y = (long int)(in[i - 3 - WITH_ALPHA] * ymax) + 0x1001L;\
167 out[i - 1 - WITH_ALPHA] = (uint16_t)CLIP(0, v, 0xFFFFL);\
168 out[i - 2 - WITH_ALPHA] = (uint16_t)CLIP(0, u, 0xFFFFL);\
169 out[i - 3 - WITH_ALPHA] = (uint16_t)CLIP(0, y, 0xFFFFL);\
170 }\
171 }\
172 } while (0)
173
174 static void raw3_to_raw2_lf (double *in, uint16_t *out, size_t n) { RAW3_TO_RAW2(double, 0); }
175 static void raw3_to_raw2_f (float *in, uint16_t *out, size_t n) { RAW3_TO_RAW2(float, 0); }
176 static void raw3a_to_raw2a_lf(double *in, uint16_t *out, size_t n) { RAW3_TO_RAW2(double, 1); }
177 static void raw3a_to_raw2a_f (float *in, uint16_t *out, size_t n) { RAW3_TO_RAW2(float, 1); }
178
179 #define CONVERT_COLOUR_SPACE(TYPE, CONV)\
180 do {\
181 size_t i, s = 3 + (size_t)a;\
182 for (i = 0; i < n; i += s)\
183 CONV(buf[i + 0], buf[i + 1], buf[i + 2], buf + i + 0, buf + i + 1, buf + i + 2);\
184 } while (0)
185
186 static void conv_yuv_to_srgb_lf(double *buf, size_t n, int a) { CONVERT_COLOUR_SPACE(double, yuv_to_srgb); }
187 static void conv_yuv_to_srgb_f (float *buf, size_t n, int a) { CONVERT_COLOUR_SPACE(float, yuv_to_srgb); }
188 static void conv_srgb_to_yuv_lf(double *buf, size_t n, int a) { CONVERT_COLOUR_SPACE(double, srgb_to_yuv); }
189 static void conv_srgb_to_yuv_f (float *buf, size_t n, int a) { CONVERT_COLOUR_SPACE(float, srgb_to_yuv); }
190 static void conv_xyz_to_srgb_lf(double *buf, size_t n, int a) { CONVERT_COLOUR_SPACE(double, ciexyz_to_srgb); }
191 static void conv_xyz_to_srgb_f (float *buf, size_t n, int a) { CONVERT_COLOUR_SPACE(float, ciexyz_to_srgb); }
192 static void conv_srgb_to_xyz_lf(double *buf, size_t n, int a) { CONVERT_COLOUR_SPACE(double, srgb_to_ciexyz); }
193 static void conv_srgb_to_xyz_f (float *buf, size_t n, int a) { CONVERT_COLOUR_SPACE(float, srgb_to_ciexyz); }
194
195 #define CHANGE_TRANSFER(TYPE, CONV)\
196 do {\
197 size_t i, s = 3 + (size_t)a;\
198 for (i = 0; i < n; i += s) {\
199 buf[i + 0] = CONV(buf[i + 0]);\
200 buf[i + 1] = CONV(buf[i + 1]);\
201 buf[i + 2] = CONV(buf[i + 2]);\
202 }\
203 } while (0)
204
205 static void conv_srgbt_to_srgb_lf(double *buf, size_t n, int a) { CHANGE_TRANSFER(double, srgb_decode); }
206 static void conv_srgbt_to_srgb_f (float *buf, size_t n, int a) { CHANGE_TRANSFER(float, srgb_decode); }
207 static void conv_srgb_to_srgbt_lf(double *buf, size_t n, int a) { CHANGE_TRANSFER(double, srgb_encode); }
208 static void conv_srgb_to_srgbt_f (float *buf, size_t n, int a) { CHANGE_TRANSFER(float, srgb_encode); }
209
210 #define CONVERT_COLOUR_SPACE_AUTO(CONV)\
211 static void\
212 conv_##CONV(enum encoding encoding, int with_alpha, void *buf, size_t n)\
213 {\
214 if (encoding == DOUBLE)\
215 conv_##CONV##_lf(buf, n, !!with_alpha);\
216 else\
217 conv_##CONV##_f(buf, n, !!with_alpha);\
218 }
219 CONVERT_COLOUR_SPACE_AUTO(yuv_to_srgb)
220 CONVERT_COLOUR_SPACE_AUTO(srgb_to_yuv)
221 CONVERT_COLOUR_SPACE_AUTO(xyz_to_srgb)
222 CONVERT_COLOUR_SPACE_AUTO(srgb_to_xyz)
223 CONVERT_COLOUR_SPACE_AUTO(srgbt_to_srgb)
224 CONVERT_COLOUR_SPACE_AUTO(srgb_to_srgbt)
225
226 static void
227 convert(struct stream *stream, struct stream *out, void *buf, size_t n)
228 {
229 enum encoding encoding = stream->encoding;
230
231 if (in_level <= 0 && out_level > 0)
232 raw0_to_raw1(buf, n);
233
234 if (in_level <= 1 && out_level > 1)
235 le_to_h_16(buf, n);
236
237 if (in_level <= 2 && out_level > 2) {
238 if (out->encoding == DOUBLE && stream->alpha)
239 raw2a_to_raw3a_lf(buf, buf, n);
240 else if (out->encoding == FLOAT && stream->alpha)
241 raw2a_to_raw3a_f(buf, buf, n);
242 else if (out->encoding == DOUBLE)
243 raw2_to_raw3_lf(buf, buf, n);
244 else if (out->encoding == FLOAT)
245 raw2_to_raw3_f(buf, buf, n);
246 encoding = out->encoding;
247 } else if (stream->encoding == FLOAT && out->encoding == DOUBLE) {
248 f_to_lf(buf, buf, n);
249 encoding = out->encoding;
250 } else if (stream->encoding == DOUBLE && out->encoding == FLOAT) {
251 lf_to_f(buf, buf, n);
252 encoding = out->encoding;
253 }
254
255 if (stream->alpha && !out->alpha) {
256 if (encoding == DOUBLE)
257 n = remove_alpha_lf(buf, n);
258 else if (encoding == FLOAT)
259 n = remove_alpha_f(buf, n);
260 else
261 n = remove_alpha_u16(buf, n);
262 } else if (!stream->alpha && out->alpha) {
263 if (encoding == DOUBLE)
264 n = add_alpha_lf(buf, buf, n);
265 else if (encoding == FLOAT)
266 n = add_alpha_f(buf, buf, n);
267 else
268 n = add_alpha_u16(buf, buf, n);
269 }
270
271 if (stream->space == CIEXYZ && out->space == YUV_NONLINEAR) {
272 conv_xyz_to_srgb(encoding, out->alpha, buf, n);
273 conv_srgb_to_srgbt(encoding, out->alpha, buf, n);
274 conv_srgb_to_yuv(encoding, out->alpha, buf, n);
275 } else if (stream->space == CIEXYZ && out->space == SRGB_NONLINEAR) {
276 conv_xyz_to_srgb(encoding, out->alpha, buf, n);
277 conv_srgb_to_srgbt(encoding, out->alpha, buf, n);
278 } else if (stream->space == CIEXYZ && out->space == SRGB) {
279 conv_xyz_to_srgb(encoding, out->alpha, buf, n);
280 } else if (stream->space == YUV_NONLINEAR && out->space == CIEXYZ) {
281 conv_yuv_to_srgb(encoding, out->alpha, buf, n);
282 conv_srgbt_to_srgb(encoding, out->alpha, buf, n);
283 conv_srgb_to_xyz(encoding, out->alpha, buf, n);
284 } else if (stream->space == YUV_NONLINEAR && out->space == SRGB_NONLINEAR) {
285 conv_yuv_to_srgb(encoding, out->alpha, buf, n);
286 } else if (stream->space == YUV_NONLINEAR && out->space == SRGB) {
287 conv_yuv_to_srgb(encoding, out->alpha, buf, n);
288 conv_srgbt_to_srgb(encoding, out->alpha, buf, n);
289 } else if (stream->space == SRGB_NONLINEAR && out->space == CIEXYZ) {
290 conv_srgbt_to_srgb(encoding, out->alpha, buf, n);
291 conv_srgb_to_xyz(encoding, out->alpha, buf, n);
292 } else if (stream->space == SRGB_NONLINEAR && out->space == YUV_NONLINEAR) {
293 conv_srgb_to_yuv(encoding, out->alpha, buf, n);
294 } else if (stream->space == SRGB_NONLINEAR && out->space == SRGB) {
295 conv_srgbt_to_srgb(encoding, out->alpha, buf, n);
296 } else if (stream->space == SRGB && out->space == CIEXYZ) {
297 conv_srgb_to_xyz(encoding, out->alpha, buf, n);
298 } else if (stream->space == SRGB && out->space == YUV_NONLINEAR) {
299 conv_srgb_to_srgbt(encoding, out->alpha, buf, n);
300 conv_srgb_to_yuv(encoding, out->alpha, buf, n);
301 } else if (stream->space == SRGB && out->space == SRGB_NONLINEAR) {
302 conv_srgb_to_srgbt(encoding, out->alpha, buf, n);
303 }
304
305 if (out_level <= 2 && in_level > 2) {
306 if (encoding == DOUBLE && out->alpha)
307 raw3a_to_raw2a_lf(buf, buf, n);
308 else if (encoding == FLOAT && out->alpha)
309 raw3a_to_raw2a_f(buf, buf, n);
310 else if (encoding == DOUBLE)
311 raw3_to_raw2_lf(buf, buf, n);
312 else if (encoding == FLOAT)
313 raw3_to_raw2_f(buf, buf, n);
314 }
315
316 if (out_level <= 1 && in_level > 1)
317 h_to_le_16(buf, n);
318
319 if (out_level <= 0 && in_level > 0)
320 raw1_to_raw0(buf, n);
321
322 ewriteall(STDOUT_FILENO, buf, n * out->chan_size, "<stdout>");
323 }
324
325 int
326 main(int argc, char *argv[])
327 {
328 struct stream stream, out;
329 const char *pixfmt;
330 void *buf = NULL;
331 size_t n;
332
333 UNOFLAGS(!argc);
334
335 eopen_stream(&stream, NULL);
336
337 memcpy(&out, &stream, sizeof(out));
338 pixfmt = stream.pixfmt;
339 while (*argv)
340 pixfmt = get_pixel_format(*argv++, pixfmt);
341 strcpy(out.pixfmt, pixfmt);
342 if (set_pixel_format(&out, NULL))
343 eprintf("output pixel format %s is not supported\n", pixfmt);
344
345 fprint_stream_head(stdout, &out);
346 efflush(stdout, "<stdout>");
347
348 if (!strcmp(stream.pixfmt, out.pixfmt)) {
349 esend_stream(&stream, STDOUT_FILENO, "<stdout>");
350 return 0;
351 }
352
353 if (stream.alpha_chan == 0)
354 in_level = 0;
355 else if (stream.endian == LITTLE)
356 in_level = 1;
357 else if (stream.encoding == UINT16)
358 in_level = 2;
359 if (out.alpha_chan == 0)
360 out_level = 0;
361 else if (out.endian == LITTLE)
362 out_level = 1;
363 else if (out.encoding == UINT16)
364 out_level = 2;
365
366 if (out.encoding == UINT16)
367 out.encoding = stream.encoding;
368
369 n = MAX(stream.chan_size, out.chan_size) * MAX(stream.n_chan, out.n_chan);
370 if (n > stream.pixel_size)
371 buf = emalloc(sizeof(stream.buf) / stream.chan_size * n);
372
373 do {
374 n = stream.ptr / stream.pixel_size * stream.n_chan;
375 if (buf)
376 memcpy(buf, stream.buf, n * stream.chan_size);
377 convert(&stream, &out, buf ? buf : stream.buf, n);
378 n *= stream.chan_size;
379 memmove(stream.buf, stream.buf + n, stream.ptr -= n);
380 } while (eread_stream(&stream, SIZE_MAX));
381 if (stream.ptr)
382 eprintf("%s: incomplete frame\n", stream.file);
383
384 free(buf);
385 return 0;
386 }