blind

suckless command-line video editing utility
git clone git://git.suckless.org/blind
Log | Files | Refs | README | LICENSE

commit 76cdf509b36bb0012cde9bc1760cdf3b26874af2
parent 51411b26324ce4a142c817428d4f34c0f94a6d94
Author: Mattias Andrée <maandree@kth.se>
Date:   Mon,  9 Jan 2017 03:51:26 +0100

Use CIE XYZ instead of sRGB

Signed-off-by: Mattias Andrée <maandree@kth.se>

Diffstat:
MREADME | 10----------
Asrc/stream.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stream.h | 28++++++++++++++++++++++++++++
Msrc/util.h | 42+++++++++++++++++++++++++++++++++++++++++-
Asrc/vu-colour-srgb.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/vu-concat.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/vu-dissolve.c | 109++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/vu-frame-to-image.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/vu-image-to-frame.c | 98++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/vu-next-frame.c | 75++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Asrc/vu-read-head.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/vu-repeat.c | 27+++++++++++++++++++--------
Msrc/vu-set-alpha.c | 154+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/vu-single-colour.c | 57+++++++++++++++++++++++++++++----------------------------
Msrc/vu-stack.c | 186++++++++++++++++++++++++++++++++++++-------------------------------------------
Asrc/vu-write-head.c | 31+++++++++++++++++++++++++++++++
16 files changed, 925 insertions(+), 368 deletions(-)

diff --git a/README b/README @@ -7,13 +7,3 @@ Rendering takes a long time, with this approach you can leverage the power of Make. Source control friendly. - - -FORMAT - -The utilities use raw RGBA video, parameters for ffmpeg are: - - -f rawvideo - -pix_fmt rgba - -s:v ${WIDTH}x${HEIGHT} - -r ${FRAME_RATE} diff --git a/src/stream.c b/src/stream.c @@ -0,0 +1,138 @@ +/* See LICENSE file for copyright and license details. */ +#include "stream.h" +#include "util.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void +einit_stream(struct stream *stream) +{ + eninit_stream(1, stream); +} + +void +eninit_stream(int status, struct stream *stream) +{ + ssize_t r; + size_t n; + char *p = NULL, *w, *h, *f, *end; + + for (stream->ptr = 0; p;) { + r = read(stream->fd, stream->buf + stream->ptr, sizeof(stream->buf) - stream->ptr); + if (r < 0) + enprintf(status, "read %s:", stream->file); + if (r == 0) + goto bad_format; + stream->ptr += (size_t)r; + p = memchr(stream->buf, '\n', stream->ptr); + } + + *p = '\0'; + w = strchr(stream->buf, ' '); + if (!w) + goto bad_format; + h = strchr(w + 1, ' '); + if (!h) + goto bad_format; + f = strchr(h + 1, ' '); + if (!f) + goto bad_format; + *w++ = *h++ = *f++ = '\0'; + + if (strlen(f) >= sizeof(stream->pixfmt)) + goto bad_format; + strcpy(stream->pixfmt, f); + errno = 0; + stream->frames = strtoul(stream->buf, &end, 10); + if (errno == ERANGE && *stream->buf != '-') + eprintf("%s: too long\n", stream->file); + if (errno || *end) + goto bad_format; + errno = 0; + stream->width = strtoul(w, &end, 10); + if (errno == ERANGE && *stream->buf != '-') + eprintf("%s: too wide\n", stream->file); + if (errno || *end) + goto bad_format; + errno = 0; + stream->height = strtoul(h, &end, 10); + if (errno == ERANGE && *stream->buf != '-') + eprintf("%s: too tall\n", stream->file); + if (errno || *end) + goto bad_format; + + n = (size_t)(p - stream->buf) + 1; + memmove(stream->buf, stream->buf + n, stream->ptr -= n); + while (stream->ptr < 5) { + r = read(stream->fd, stream->buf + stream->ptr, sizeof(stream->buf) - stream->ptr); + if (r < 0) + enprintf(status, "read %s:", stream->file); + if (r == 0) + goto bad_format; + stream->ptr += (size_t)r; + } + if (stream->buf[0] != '\0' || + stream->buf[1] != 'u' || stream->buf[2] != 'i' || + stream->buf[3] != 'v' || stream->buf[4] != 'f') + goto bad_format; + memmove(stream->buf, stream->buf + 5, stream->ptr -= 5); + + enset_pixel_size(status, stream); + + return; +bad_format: + enprintf(status, "%s: file format not supported%s\n", stream->file); +} + + +int +set_pixel_size(struct stream *stream) +{ + if (!strcmp(stream->pixfmt, "xyza")) + stream->pixel_size = 4 * sizeof(double); + else + return -1; + return 0; +} + +void +eset_pixel_size(struct stream *stream) +{ + enset_pixel_size(1, stream); +} + +void +enset_pixel_size(int status, struct stream *stream) +{ + if (set_pixel_size(stream)) + enprintf(status, "file %s uses unsupported pixel format: %s\n", + stream->file, stream->pixfmt); +} + + +void +fprint_stream_head(FILE *fp, struct stream *stream) +{ + fprintf(fp, "%zu %zu %zu %s\n%cuivf", + stream->frames, stream->width, stream->height, stream->pixfmt, 0); +} + + +size_t eread_stream(struct stream *stream, size_t n) +{ + return enread_stream(1, stream, n); +} + +size_t enread_stream(int status, struct stream *stream, size_t n) +{ + ssize_t r = read(stream->fd, stream->buf + stream->ptr, + sizeof(stream->buf) - stream->ptr < n ? + sizeof(stream->buf) - stream->ptr : n); + if (r < 0) + enprintf(status, "read %s:", stream->file); + stream->ptr += (size_t)r; + return (size_t)r; +} diff --git a/src/stream.h b/src/stream.h @@ -0,0 +1,28 @@ +/* See LICENSE file for copyright and license details. */ +#include <stddef.h> +#include <stdio.h> + +struct stream +{ + size_t frames; + size_t width; + size_t height; + size_t pixel_size; + char pixfmt[32]; + int fd; + size_t ptr; + char buf[4096]; + const char *file; +}; + +void einit_stream(struct stream *stream); +void eninit_stream(int status, struct stream *stream); + +int set_pixel_size(struct stream *stream); +void eset_pixel_size(struct stream *stream); +void enset_pixel_size(int status, struct stream *stream); + +void fprint_stream_head(FILE *fp, struct stream *stream); + +size_t eread_stream(struct stream *stream, size_t n); +size_t enread_stream(int status, struct stream *stream, size_t n); diff --git a/src/util.h b/src/util.h @@ -1,7 +1,11 @@ /* See LICENSE file for copyright and license details. */ - +#include <errno.h> #include <math.h> #include <stdio.h> +#include <stdlib.h> + +#define D65_XYY_X 0.312726871026564878786047074755 +#define D65_XYY_Y 0.329023206641284038376227272238 #define ELEMENTSOF(ARRAY) (sizeof(ARRAY) / sizeof(*(ARRAY))) @@ -31,6 +35,21 @@ DEF_STR_TO_INT(toi, int, tolli, long long int) #define toju tollu #define toji tolli +static inline int +tolf(const char *s, double *out) +{ + char *end; + errno = 0; + *out = strtod(s, &end); + if (errno) { + return -1; + } else if (*end) { + errno = EINVAL; + return -1; + } + return 0; +} + int fshut(FILE *fp, const char *fname); void enfshut(int status, FILE *fp, const char *fname); void efshut(FILE *fp, const char *fname); @@ -58,3 +77,24 @@ srgb_decode(double t) t = t <= 0.0031306684425217108 * 12.92 ? t / 12.92 : pow((t + 0.055) / 1.055, 2.4); return t * sign; } + +static inline void +ciexyz_to_srgb(double x, double y, double z, double *r, double *g, double *b) +{ +#define MULTIPLY(CX, CY, CZ) ((CX) * x + (CY) * y + (CZ) * z) + *r = MULTIPLY(3.240446254647737500675930277794, -1.537134761820080575134284117667, -0.498530193022728718155178739835); + *g = MULTIPLY(-0.969266606244679751469561779231, 1.876011959788370209167851498933, 0.041556042214430065351304932619); + *b = MULTIPLY(0.055643503564352832235773149705, -0.204026179735960239147729566866, 1.057226567722703292062647051353); +#undef MULTIPLY +} + +static inline void +srgb_to_ciexyz(double r, double g, double b, double *x, double *y, double *z) +{ +#define MULTIPLY(CR, CG, CB) ((CR) * r + (CG) * g + (CB) * b) + *x = MULTIPLIY(0.412457445582367576708548995157, 0.357575865245515878143578447634, 0.180437247826399665973085006954); + *y = MULTIPLIY(0.212673370378408277403536885686, 0.715151730491031756287156895269, 0.072174899130559869164791564344); + *z = MULTIPLIY(0.019333942761673460208893260415, 0.119191955081838593666354597644, 0.950302838552371742508739771438); +#undef MULTIPLY + +} diff --git a/src/vu-colour-srgb.c b/src/vu-colour-srgb.c @@ -0,0 +1,57 @@ +/* See LICENSE file for copyright and license details. */ +#include "arg.h" +#include "util.h" + +static void +usage(void) +{ + eprintf("usage: %s [-d depth] [-l] red green blue\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + unsigned long long int max; + double red, green, blue, X, Y, Z; + int depth = 8; + int linear = 0; + + ARGBEGIN { + case 'd': + if (toi(EARGF(usage()), 1, 64, &depth)) + eprintf("argument of -d must be an integer in [1, 64]\n"); + break; + case 'l': + linear = 1; + break; + default: + usage(); + } ARGEND; + + if (argc != 3) + usage(); + + if (tolf(argv[0], &red)) + eprintf("the X value must be a floating-point value\n"); + if (tolf(argv[1], &green)) + eprintf("the Y value must be a floating-point value\n"); + if (tolf(argv[2], &blue)) + eprintf("the Z value must be a floating-point value\n"); + + max = 1ULL << (depth - 1); + max |= max - 1; + red /= max; + green /= max; + blue /= max; + if (!linear) { + red = srgb_decode(red); + green = srgb_decode(green); + blue = srgb_decode(blue); + } + + srgb_to_ciexyz(red, green, blue, &X, &Y, &Z); + printf("%lf %lf %lf\n", X, Y, Z); + efshut(stdout, "<stdout>"); + + return 0; +} diff --git a/src/vu-concat.c b/src/vu-concat.c @@ -0,0 +1,65 @@ +/* See LICENSE file for copyright and license details. */ +#include "arg.h" +#include "stream.h" +#include "util.h" + +#include <fcntl.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> + +static void +usage(void) +{ + eprintf("usage: %s first-stream ... last-stream\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + struct stream stream, refstream; + size_t ptr; + ssize_t r; + int i; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (argc < 2) + usage(); + + for (i = 0; i < argc; i++) { + stream.file = argv[i]; + stream.fd = open(stream.file, O_RDONLY); + if (stream.fd < 0) + eprintf("open %s:", stream.file); + einit_stream(&stream); + + if (!i) { + memcpy(&refstream, &stream, sizeof(stream)); + fprint_stream_head(stdout, &stream); + fflush(stdout); + if (ferror(stdout)) + eprintf("<stdout>:"); + } else { + if (stream.width != refstream.width || stream.height != refstream.height) + eprintf("videos do not have the same geometry\n"); + if (strcmp(stream.pixfmt, refstream.pixfmt)) + eprintf("videos use incompatible pixel formats\n"); + } + + for (; eread_stream(&stream, SIZE_MAX); stream.ptr = 0) { + for (ptr = 0;; ptr += (size_t)r) { + r = write(STDOUT_FILENO, stream.buf + ptr, stream.ptr - ptr); + if (r < 0) + eprintf("write <stdout>"); + } + } + + close(stream.fd); + } + + return 0; +} diff --git a/src/vu-dissolve.c b/src/vu-dissolve.c @@ -1,51 +1,52 @@ /* See LICENSE file for copyright and license details. */ #include "arg.h" +#include "stream.h" #include "util.h" -#include <fcntl.h> -#include <inttypes.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> #include <string.h> #include <unistd.h> -static unsigned int -clip(unsigned int value) +static void +usage(void) { - return value > 255 ? 255 : value; + eprintf("usage: %s [-r]\n", argv0); } static void -usage(void) +process_xyza(struct stream *stream, size_t n, size_t f, size_t fm) +{ + size_t i; + double a; + for (i = 0; i < n; i += stream->pixel_size) { + a = ((double *)(stream->buf + i))[3]; + a = a * (double)f / fm; + ((double *)(stream->buf + i))[3] = a; + } +} + +static void +process_xyza_r(struct stream *stream, size_t n, size_t f, size_t fm) { - eprintf("usage: %s [-r] -f frames -w width -h height\n", argv0); + size_t i; + double a; + for (i = 0; i < n; i += stream->pixel_size) { + a = ((double *)(stream->buf + i))[3]; + a = a * (double)(fm - f) / fm; + ((double *)(stream->buf + i))[3] = a; + } } int main(int argc, char *argv[]) { + struct stream stream; int reverse = 0; - size_t frames = 0, width = 0, height = 0; - unsigned char buf[8096]; - size_t frame, h, w, max; - size_t ptr = 0, n, i, fm; + size_t f, h, w; + size_t n, i, fm; ssize_t r; - double a; + void (*process)(struct stream *stream, size_t n, size_t f, size_t fm); ARGBEGIN { - case 'f': - if (tozu(EARGF(usage()), 2, SIZE_MAX, &frames)) - eprintf("argument of -f must be an integer in [2, %zu]\n", SIZE_MAX); - break; - case 'w': - if (tozu(EARGF(usage()), 1, SIZE_MAX, &width)) - eprintf("argument of -w must be an integer in [1, %zu]\n", SIZE_MAX); - break; - case 'h': - if (tozu(EARGF(usage()), 1, SIZE_MAX, &height)) - eprintf("argument of -h must be an integer in [1, %zu]\n", SIZE_MAX); - break; case 'r': reverse = 1; break; @@ -53,42 +54,36 @@ main(int argc, char *argv[]) usage(); } ARGEND; - if (argc || !frames || !width || !height) + if (argc) usage(); - fm = frames - 1; - for (frame = 0; frame < frames; frame++) { - for (h = height; h--;) { - for (w = width << 2; w; w -= n) { - max = sizeof(buf) - ptr < w ? sizeof(buf) - ptr : w; - r = read(STDIN_FILENO, buf + ptr, max); - if (r < 0) - eprintf("read <stdin>:"); - if (r == 0) - eprintf("content is shorter than specified\n"); - n = ptr += (size_t)r; - n -= n & 3; - if (reverse) { - for (i = 0; i < n; i += 4) { - a = buf[i + 3]; - a = a * (double)frame / fm; - a += 0.5; - buf[i + 3] = clip((unsigned int)a); - } - } else { - for (i = 0; i < n; i += 4) { - a = buf[i + 3]; - a = a * (double)(fm - frame) / fm; - a += 0.5; - buf[i + 3] = clip((unsigned int)a); - } - } + stream.fd = STDIN_FILENO; + stream.file = "<stdin>"; + einit_stream(&stream); + fprint_stream_head(stdout, &stream); + fflush(stdout); + if (ferror(stdout)) + eprintf("<stdout>:"); + + if (!strcmp(stream.pixfmt, "xyza")) + process = reverse ? process_xyza_r : process_xyza; + else + eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt); + + fm = stream.frames - 1; + for (f = 0; f < stream.frames; f++) { + for (h = stream.height; h--;) { + for (w = stream.width * stream.pixel_size; w; w -= n) { + if (!eread_stream(&stream, w)) + eprintf("<stdin>: file is truncated\n"); + n = stream.ptr - (stream.ptr % stream.pixel_size); + process(&stream, n, f, fm); for (i = 0; i < n; i += (size_t)r) { - r = write(STDOUT_FILENO, buf + i, n - i); + r = write(STDOUT_FILENO, stream.buf + i, n - i); if (r < 0) eprintf("write <stdout>:"); } - memmove(buf, buf + n, ptr -= n); + memmove(stream.buf, stream.buf + n, stream.ptr -= n); } } } diff --git a/src/vu-frame-to-image.c b/src/vu-frame-to-image.c @@ -1,66 +1,148 @@ /* See LICENSE file for copyright and license details. */ #include "arg.h" +#include "stream.h" #include "util.h" #include <inttypes.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> +static int luma_warning_triggered = 0; +static int gamut_warning_triggered = 0; +static int alpha_warning_triggered = 0; + static void usage(void) { - eprintf("usage: %s -w width -h height\n", argv0); + eprintf("usage: %s [-d depth]\n", argv0); +} + +static void +write_pixel(double R, double G, double B, double A, int bytes, unsigned long long int max) +{ + unsigned long long int colours[4]; + unsigned char buf[4 * 8]; + int i, j, k, bm = bytes - 1; + size_t ptr, n; + ssize_t r; + + if (R < 0 || G < 0 || B < 0 || R > 1 || G > 1 || B > 1) { + if (gamut_warning_triggered) { + gamut_warning_triggered = 1; + weprintf("warning: out-of-gamut colour detected\n"); + } + ; /* TODO gamut */ + R = R < 0 ? 0 : R > 1 ? 1 : R; + G = G < 0 ? 0 : G > 1 ? 1 : G; + B = B < 0 ? 0 : B > 1 ? 1 : B; + } + + if (A < 0 || A > 1) { + if (alpha_warning_triggered) { + alpha_warning_triggered = 1; + weprintf("warning: alpha values truncated\n"); + } + A = A < 0 ? 0 : 1; + } + + colours[0] = srgb_encode(R) * max + 0.5; + colours[1] = srgb_encode(G) * max + 0.5; + colours[2] = srgb_encode(B) * max + 0.5; + colours[3] = A * max + 0.5; + + for (i = k = 0; i < 4; i++) { + for (j = 0; j < bytes; j++, k++) { + buf[k + bm - j] = (unsigned char)(colours[j]); + colours[j] >>= 8; + } + } + + n = (size_t)bytes * 4; + for (ptr = 0; ptr < n; ptr += (size_t)r) { + r = write(STDOUT_FILENO, buf + ptr, n - ptr); + if (r < 0) + eprintf("write <stdout>:"); + } +} + +static void +process_xyza(struct stream *stream, size_t n, int bytes, unsigned long long int max) +{ + size_t i; + double X, Y, Z, A, R, G, B; + for (i = 0; i < n; i += stream->pixel_size) { + X = ((double *)(stream->buf + i))[0]; + Y = ((double *)(stream->buf + i))[1]; + Z = ((double *)(stream->buf + i))[2]; + A = ((double *)(stream->buf + i))[3]; + + if (Y < 0 || Y > 1) { + if (luma_warning_triggered) { + luma_warning_triggered = 1; + weprintf("warning: %s colour detected\n", + Y < 0 ? "subblack" : "superwhite"); + } + } + + srgb_to_ciexyz(X, Y, Z, &R, &G, &B); + write_pixel(R, G, B, A, bytes, max); + } } int main(int argc, char *argv[]) { - size_t width = 0, height = 0; - size_t p, n; - ssize_t r; - char buf[8096]; + struct stream stream; + int depth = 16, bytes; + unsigned long long int max; + size_t n; + void (*process)(struct stream *stream, size_t n, int bytes, unsigned long long int max); ARGBEGIN { - case 'w': - if (tozu(EARGF(usage()), 1, SIZE_MAX, &width)) - eprintf("argument of -w must be an integer in [1, %zu]\n", SIZE_MAX); - break; - case 'h': - if (tozu(EARGF(usage()), 1, SIZE_MAX, &height)) - eprintf("argument of -h must be an integer in [1, %zu]\n", SIZE_MAX); + case 'd': + if (toi(EARGF(usage()), 1, 64, &depth)) + eprintf("argument of -d must be an integer in [1, 64]\n"); break; default: usage(); } ARGEND; - if (!width || !height || argc) + if (argc) usage(); + stream.fd = STDIN_FILENO; + stream.file = "<stdin.h>"; + einit_stream(&stream); + + max = 1ULL << (depth - 1); + max |= max - 1; + for (bytes = 1; bytes * 8 < depth; bytes++); + + if (!strcmp(stream.pixfmt, "xyza")) + process = process_xyza; + else + eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt); + printf("P7\n" "WIDTH %zu\n" "HEIGHT %zu\n" - "DEPTH 4\n" - "MAXVAL 255\n" + "DEPTH 4\n" /* Depth actually means channels */ + "MAXVAL %llu\n" "TUPLTYPE RGB_ALPHA\n" - "ENDHDR\n", width, height); + "ENDHDR\n", stream.width, stream.height, max); fflush(stdout); if (ferror(stdout)) eprintf("<stdout>:"); - for (n = 0;;) { - for (p = 0; p < n;) { - r = write(STDOUT_FILENO, buf + p, n - p); - if (r < 0) - eprintf("write <stdin>:"); - p += (size_t)r; - } - r = read(STDIN_FILENO, buf, sizeof(buf)); - if (r < 0) - eprintf("read <stdin>:"); - if (r == 0) + for (;;) { + n = stream.ptr; + n -= n % stream.pixel_size; + process(&stream, n, bytes, max); + memmove(stream.buf, stream.buf + n, stream.ptr -= n); + if (!eread_stream(&stream, SIZE_MAX)) break; - n = (size_t)r; } return 0; diff --git a/src/vu-image-to-frame.c b/src/vu-image-to-frame.c @@ -6,21 +6,21 @@ #include <string.h> #include <unistd.h> -static char * -xmemmem(char *h, const char *n, size_t hn, size_t nn) +static double +get_value(void *buffer) { - char *p, *end; - if (nn > hn) - return NULL; - end = h + (hn - nn + 1); - for (p = h; p != end; p++) { - p = memchr(p, *n, (size_t)(end - p)); - if (!p) - return NULL; - if (!memcmp(p, n, nn)) - return p; - } - return NULL; + unsigned char *buf = buffer; + unsigned long int value; + double ret; + value = (unsigned long int)(buf[0]) << 12; + value += (unsigned long int)(buf[1]) << 8; + value += (unsigned long int)(buf[2]) << 4; + value += (unsigned long int)(buf[3]); + ret = value; + value = 1UL << 15; + value |= value - 1; + ret /= value; + return ret; } static void @@ -37,9 +37,12 @@ main(int argc, char *argv[]) pid_t pid; int status; char buf[8096]; - size_t ptr, n; + size_t ptr, ptw, n; char *p; ssize_t r; + double red, green, blue, pixel[4]; + char width[3 * sizeof(size_t) + 1] = {0}; + char height[3 * sizeof(size_t) + 1] = {0}; ARGBEGIN { default: @@ -73,10 +76,11 @@ main(int argc, char *argv[]) if (dup2(pipe_rw[1], STDOUT_FILENO) < 0) eprintf("dup2:"); close(pipe_rw[1]); - execlp("convert", "convert", "-", "-depth", "8", "-alpha", "activate", "pam:-", NULL); + /* XXX Is there a way to convert directly to raw XYZ? (Would avoid gamut truncation) */ + execlp("convert", "convert", "-", "-depth", "32", "-alpha", "activate", "pam:-", NULL); eprintf("exec convert:"); } - + close(pipe_rw[1]); for (ptr = 0;;) { @@ -84,24 +88,56 @@ main(int argc, char *argv[]) if (r < 0) eprintf("read <subprocess>:"); if (r == 0) - break; + eprintf("convertion failed\n"); ptr += (size_t)r; - p = xmemmem(buf, "\nENDHDR\n", ptr, sizeof("\nENDHDR\n") - 1); - if (!p) - continue; - p += sizeof("\nENDHDR\n") - 1; - n = (size_t)(p - buf); - memmove(buf, buf + n, ptr - n); - n = ptr - n; - break; + + for (;;) { + p = memchr(buf, '\n', ptr); + if (!p) { + if (ptr == sizeof(buf)) + eprintf("convertion failed\n"); + break; + } + *p++ = '\0'; + if (strstr(buf, "WIDTH ") == buf) { + if (*width || !buf[6] || strlen(buf + 6) >= sizeof(width)) + eprintf("convertion failed\n"); + strcpy(width, buf + 6); + } else if (strstr(buf, "HEIGHT ") == buf) { + if (*height || !buf[7] || strlen(buf + 7) >= sizeof(height)) + eprintf("convertion failed\n"); + strcpy(height, buf + 7); + } else if (!strcmp(buf, "ENDHDR")) { + memmove(buf, p, ptr -= (size_t)(p - buf)); + goto header_done; + } + memmove(buf, p, ptr -= (size_t)(p - buf)); + } } +header_done: + + if (!*width || !*height) + eprintf("convertion failed\n"); + + printf("%s %s xyza\n%cuivf", width, height, 0); + fflush(stdout); + if (ferror(stdout)) + eprintf("<stdout>:"); for (;;) { - for (ptr = 0; ptr < n;) { - r = write(STDOUT_FILENO, buf + ptr, n - ptr); - if (r < 0) - eprintf("write <stdout>:"); - ptr += (size_t)r; + for (ptr = 0; ptr + 15 < n; ptr += 16) { + red = get_value(buf + ptr + 0); + green = get_value(buf + ptr + 4); + blue = get_value(buf + ptr + 8); + pixel[3] = get_value(buf + ptr + 12); + + srgb_to_ciexyz(red, green, blue, pixel + 0, pixel + 1, pixel + 2); + + for (ptw = 0; ptw < sizeof(pixel); ptw += (size_t)r) { + r = write(STDOUT_FILENO, (char *)pixel + ptw, sizeof(pixel) - ptw); + if (r < 0) + eprintf("write <stdout>:"); + } } r = read(pipe_rw[0], buf, sizeof(buf)); if (r < 0) diff --git a/src/vu-next-frame.c b/src/vu-next-frame.c @@ -1,10 +1,10 @@ /* See LICENSE file for copyright and license details. */ #include "arg.h" +#include "stream.h" #include "util.h" #include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> +#include <string.h> #include <unistd.h> #define eprintf(...) enprintf(2, __VA_ARGS__) @@ -12,56 +12,73 @@ static void usage(void) { - eprintf("usage: %s -w width -h height\n", argv0); + eprintf("usage: %s width height pixel-format ...\n", argv0); } int main(int argc, char *argv[]) { - size_t width = 0, height = 0; - size_t p, n, w, max; + struct stream stream; + size_t ptr, n, w; ssize_t r; - char buf[8096]; - int anything = 0; + int i, anything = 0; + char *p; ARGBEGIN { - case 'w': - if (tozu(EARGF(usage()), 1, SIZE_MAX, &width)) - eprintf("argument of -w must be an integer in [1, %zu]\n", SIZE_MAX); - break; - case 'h': - if (tozu(EARGF(usage()), 1, SIZE_MAX, &height)) - eprintf("argument of -h must be an integer in [1, %zu]\n", SIZE_MAX); - break; default: usage(); } ARGEND; - if (!width || !height || argc) + if (argc < 3) usage(); - while (height) { - height--; - for (w = width << 2; w;) { - max = sizeof(buf) < w ? sizeof(buf) : w; - r = read(STDIN_FILENO, buf, max); - if (r < 0) - eprintf("read <stdin>:"); - if (r == 0) + stream.frames = 1; + stream.fd = STDIN_FILENO; + stream.file = "<stdin>"; + stream.pixfmt[0] = '\0'; + + if (tozu(argv[0], 1, SIZE_MAX, &stream.width)) + eprintf("the width must be an integer in [1, %zu]\n", SIZE_MAX); + if (tozu(argv[1], 1, SIZE_MAX, &stream.height)) + eprintf("the height must be an integer in [1, %zu]\n", SIZE_MAX); + argv += 2, argc -= 2; + + n = (size_t)argc - 1; + for (i = 0; i < argc; i++) + n += strlen(argv[i]); + if (n < sizeof(stream.pixfmt)) { + p = stpcpy(stream.pixfmt, argv[0]); + for (i = 1; i < argc; i++) { + *p++ = ' '; + p = stpcpy(p, argv[i]); + } + } + + eset_pixel_size(&stream); + + fprint_stream_head(stdout, &stream); + fflush(stdout); + if (ferror(stdout)) + eprintf("<stdout>:"); + + while (stream.height) { + stream.height--; + for (w = stream.width * stream.pixel_size; w; w -= n) { + stream.ptr = 0; + n = eread_stream(&stream, w); + if (n == 0) goto done; anything = 1; - w -= n = (size_t)r; - for (p = 0; p < n;) { - r = write(STDOUT_FILENO, buf + p, n - p); + for (ptr = 0; ptr < stream.ptr; ptr += (size_t)r) { + r = write(STDOUT_FILENO, stream.buf + ptr, stream.ptr - ptr); if (r < 0) eprintf("write <stdin>:"); - p += (size_t)r; } } } done: - if (height || w) + if (stream.height || w) eprintf("incomplete frame\n"); return !anything; diff --git a/src/vu-read-head.c b/src/vu-read-head.c @@ -0,0 +1,78 @@ +/* See LICENSE file for copyright and license details. */ +#include "arg.h" +#include "stream.h" +#include "util.h" + +#include <ctype.h> +#include <unistd.h> + +static void +usage(void) +{ + eprintf("usage: %s\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + char buf[2 + 3 * sizeof(size_t) + sizeof(((struct stream *)0)->pixfmt)]; + char magic[] = {'\0', 'u', 'i', 'v', 'f'}; + char b; + char *p, *end; + size_t i, ptr; + ssize_t r; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (!argc) + usage(); + + for (ptr = 0; ptr < sizeof(buf);) { + r = read(STDIN_FILENO, buf + ptr, 1); + if (r < 0) + eprintf("read <stdin>:"); + if (r == 0) + goto bad_format; + if (buf[ptr++] == '\n') + break; + } + if (ptr == sizeof(buf)) + goto bad_format; + + for (i = 0; i < 5; i++) { + r = read(STDIN_FILENO, &b, 1); + if (r < 0) + eprintf("read <stdin>:"); + if (!r || b != magic[i]) + goto bad_format; + } + + end = (p = buf) + ptr; + for (i = 0; i < 2; i++) { + if (!isdigit(*p)) + goto bad_format; + while (isdigit(*p)) p++; + if (*p++ != ' ') + goto bad_format; + } + while (isalnum(*p) || *p == ' ') { + if (p[0] == ' ' && p[-1] == ' ') + goto bad_format; + p++; + } + if (p[-1] == ' ' || p[0] != '\n') + goto bad_format; + + for (i = 0; i < ptr; i += (size_t)r) { + r = write(STDOUT_FILENO, buf + i, ptr - i); + if (r < 0) + eprintf("write <stdout>:"); + } + + return 0; +bad_format: + eprintf("<stdin>: file format not supported\n"); +} diff --git a/src/vu-repeat.c b/src/vu-repeat.c @@ -1,5 +1,6 @@ /* See LICENSE file for copyright and license details. */ #include "arg.h" +#include "stream.h" #include "util.h" #include <fcntl.h> @@ -15,9 +16,8 @@ usage(void) int main(int argc, char *argv[]) { - struct stat st; + struct stream stream; size_t count, ptr, n, ptw; - int fd; ssize_t r; char buf[BUFSIZ]; @@ -32,14 +32,25 @@ main(int argc, char *argv[]) if (tozu(argv[0], 0, SIZE_MAX, &count)) eprintf("the count must be an integer in [0, %zu]\n", SIZE_MAX); - fd = open(argv[1], O_RDONLY); - if (fd < 0) - eprintf("open %s:", argv[1]); + stream.file = argv[1]; + stream.fd = open(stream.file, O_RDONLY); + if (stream.fd < 0) + eprintf("open %s:", stream.file); + einit_stream(&stream); + fprint_stream_head(stdout, &stream); + fflush(stdout); + if (ferror(stdout)) + eprintf("<stdout>:"); while (count--) { - posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); + for (ptw = 0; ptw < stream.ptr;) { + r = write(STDOUT_FILENO, stream.buf + ptw, stream.ptr - ptw); + if (r < 0) + eprintf("write <stdout>:"); + ptw += (size_t)r; + } for (ptr = 0;;) { - r = pread(fd, buf, sizeof(buf), ptr); + r = pread(stream.fd, buf, sizeof(buf), ptr); if (r < 0) eprintf("pread %s:", argv[1]); else if (r == 0) @@ -54,6 +65,6 @@ main(int argc, char *argv[]) } } - close(fd); + close(stream.fd); return 0; } diff --git a/src/vu-set-alpha.c b/src/vu-set-alpha.c @@ -1,12 +1,10 @@ /* See LICENSE file for copyright and license details. */ #include "arg.h" +#include "stream.h" #include "util.h" -#include <inttypes.h> #include <fcntl.h> #include <stdint.h> -#include <stdio.h> -#include <stdlib.h> #include <string.h> #include <unistd.h> @@ -16,19 +14,39 @@ usage(void) eprintf("usage: %s [-i] colour-stream alpha-stream\n", argv0); } +static void +process_xyza(struct stream *colour, struct stream *alpha, size_t n) +{ + size_t i; + double a; + for (i = 0; i < n; i += colour->pixel_size) { + a = ((double *)(alpha->buf + i))[1]; + a *= ((double *)(alpha->buf + i))[3]; + ((double *)(colour->buf + i))[3] *= a; + } +} + +static void +process_xyza_i(struct stream *colour, struct stream *alpha, size_t n) +{ + size_t i; + double a; + for (i = 0; i < n; i += colour->pixel_size) { + a = 1 - ((double *)(alpha->buf + i))[1]; + a *= ((double *)(alpha->buf + i))[3]; + ((double *)(colour->buf + i))[3] *= a; + } +} + int main(int argc, char *argv[]) { int invert = 0; - int fd_colour = -1; - int fd_alpha = -1; - unsigned char buf_colour[1024]; - unsigned char buf_alpha[1024]; - size_t ptr_colour = 0; - size_t ptr_alpha = 0; + struct stream colour; + struct stream alpha; ssize_t r; size_t i, n; - unsigned long int re, gr, bl, a1, a2; + void (*process)(struct stream *colour, struct stream *alpha, size_t n); ARGBEGIN { case 'i': @@ -41,96 +59,82 @@ main(int argc, char *argv[]) if (argc != 2) usage(); - fd_colour = open(argv[0], O_RDONLY); - if (fd_colour < 0) - eprintf("open %s:", argv[0]); + colour.file = argv[0]; + colour.fd = open(colour.file, O_RDONLY); + if (colour.fd < 0) + eprintf("open %s:", colour.file); + einit_stream(&colour); + + alpha.file = argv[1]; + alpha.fd = open(alpha.file, O_RDONLY); + if (alpha.fd < 0) + eprintf("open %s:", alpha.file); + einit_stream(&alpha); + + if (colour.width != alpha.width || colour.height != alpha.height) + eprintf("videos do not have the same geometry\n"); + if (colour.pixel_size != alpha.pixel_size) + eprintf("videos use incompatible pixel formats\n"); - fd_alpha = open(argv[1], O_RDONLY); - if (fd_alpha < 0) - eprintf("open %s:", argv[1]); + if (!strcmp(colour.pixfmt, "xyza")) + process = invert ? process_xyza_i : process_xyza; + else + eprintf("pixel format %s is not supported, try xyza\n", colour.pixfmt); for (;;) { - r = read(fd_colour, buf_colour + ptr_colour, sizeof(buf_colour) - ptr_colour); - if (r < 0) { - eprintf("read %s:", argv[0]); - } else if (r == 0) { - close(fd_colour); - fd_colour = -1; + if (colour.ptr < sizeof(colour.buf) && !eread_stream(&colour, SIZE_MAX)) { + close(colour.fd); + colour.fd = -1; break; - } else { - ptr_colour += (size_t)r; } - - r = read(fd_alpha, buf_alpha + ptr_alpha, sizeof(buf_alpha) - ptr_alpha); - if (r < 0) { - eprintf("read %s:", argv[0]); - } else if (r == 0) { - close(fd_alpha); - fd_alpha = -1; + if (alpha.ptr < sizeof(alpha.buf) && !eread_stream(&alpha, SIZE_MAX)) { + close(colour.fd); + alpha.fd = -1; break; - } else { - ptr_alpha += (size_t)r; } - n = ptr_colour < ptr_alpha ? ptr_colour : ptr_alpha; - n -= n & 3; - ptr_colour -= n; - ptr_alpha -= n; - - for (i = 0; i < n; i += 4) { - re = buf_alpha[i + 0]; - gr = buf_alpha[i + 1]; - bl = buf_alpha[i + 2]; - a1 = buf_alpha[i + 3]; - a2 = buf_colour[i + 3]; - re += gr + bl; - if (invert) - re = 765 - re; - a1 *= a2 * re; - a1 = (a1 * 2 + 3 * 255 * 255) / (3 * 255 * 255 * 2); - buf_colour[i + 3] = a1; - } + n = colour.ptr < alpha.ptr ? colour.ptr : alpha.ptr; + n -= n % colour.pixel_size; + colour.ptr -= n; + alpha.ptr -= n; - for (i = 0; i < n; i++) { - r = write(STDOUT_FILENO, buf_colour + i, n - i); + process(&colour, &alpha, n); + + for (i = 0; i < n; i += (size_t)r) { + r = write(STDOUT_FILENO, colour.buf + i, n - i); if (r < 0) eprintf("write <stdout>:"); - i += (size_t)r; } - if ((n & 3) || ptr_colour != ptr_alpha) { - memmove(buf_colour, buf_colour + n, ptr_colour); - memmove(buf_alpha, buf_alpha + n, ptr_alpha); + if ((n & 3) || colour.ptr != alpha.ptr) { + memmove(colour.buf, colour.buf + n, colour.ptr); + memmove(alpha.buf, alpha.buf + n, alpha.ptr); } } - if (fd_alpha >= 0) - close(fd_alpha); + if (alpha.fd >= 0) + close(alpha.fd); - while (ptr_colour) { - r = write(STDOUT_FILENO, buf_colour, ptr_colour); + for (i = 0; i < colour.ptr; i += (size_t)r) { + r = write(STDOUT_FILENO, colour.buf + i, colour.ptr - i); if (r < 0) eprintf("write <stdout>:"); - ptr_colour -= (size_t)r; } - if (fd_colour >= 0) { + if (colour.fd >= 0) { for (;;) { - r = read(fd_colour, buf_colour, sizeof(buf_colour)); - if (r < 0) { - eprintf("read %s:", argv[0]); - } else if (r == 0) { - close(fd_colour); - fd_colour = -1; + colour.ptr = 0; + if (!eread_stream(&colour, SIZE_MAX)) { + close(colour.fd); + colour.fd = -1; break; - } else { - n = (size_t)r; } - r = write(STDOUT_FILENO, buf_colour, n); - if (r < 0) - eprintf("write <stdout>:"); - n -= (size_t)r; + for (i = 0; i < colour.ptr; i += (size_t)r) { + r = write(STDOUT_FILENO, colour.buf + i, colour.ptr - i); + if (r < 0) + eprintf("write <stdout>:"); + } } } diff --git a/src/vu-single-colour.c b/src/vu-single-colour.c @@ -3,25 +3,23 @@ #include "util.h" #include <inttypes.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> #include <unistd.h> +typedef double pixel_t[4]; + static void usage(void) { - eprintf("usage: %s [-f frames] -w width -h height red green blue [alpha]\n", argv0); + eprintf("usage: %s [-f frames] -w width -h height (X Y Z | Y) [alpha]\n", argv0); } int main(int argc, char *argv[]) { - int red, green, blue, alpha = 255; + double X, Y, Z, alpha = 1; size_t width = 0, height = 0, frames = 1; - unsigned char pixel[4]; size_t x, y, n; - int32_t buf[1024]; + pixel_t buf[1024]; ssize_t r; ARGBEGIN { @@ -41,36 +39,39 @@ main(int argc, char *argv[]) usage(); } ARGEND; - if (!width || !height || argc < 3 || argc > 4) + if (!width || !height || !argc || argc > 4) usage(); - if (toi(argv[0], 0, 255, &red)) - eprintf("the red value must be an integer in [0, 255]\n"); - if (toi(argv[1], 0, 255, &green)) - eprintf("the green value must be an integer in [0, 255]\n"); - if (toi(argv[2], 0, 255, &blue)) - eprintf("the blue value must be an integer in [0, 255]\n"); - if (argc > 3 && toi(argv[3], 0, 255, &alpha)) - eprintf("the alpha value must be an integer in [0, 255]\n"); - - pixel[0] = (unsigned char)red; - pixel[1] = (unsigned char)green; - pixel[2] = (unsigned char)blue; - pixel[3] = (unsigned char)alpha; + if (argc < 3) { + X = D65_XYY_X / D65_XYY_Y; + Z = 1 / D65_XYY_Y - 1 - X; + if (tolf(argv[1], &Y)) + eprintf("the Y value must be a floating-point value\n"); + } else { + if (tolf(argv[0], &X)) + eprintf("the X value must be a floating-point value\n"); + if (tolf(argv[1], &Y)) + eprintf("the Y value must be a floating-point value\n"); + if (tolf(argv[2], &Z)) + eprintf("the Z value must be a floating-point value\n"); + } + if (!(argc & 1) && tolf(argv[argc - 1], &alpha)) + eprintf("the alpha value must be a floating-point value\n"); - for (x = 0; x < ELEMENTSOF(buf); x++) - buf[x] = *(int32_t *)(void *)pixel; + for (x = 0; x < ELEMENTSOF(buf); x++) { + buf[x][0] = X; + buf[x][1] = Y; + buf[x][2] = Z; + buf[x][3] = alpha; + } while (frames--) { for (y = height; y--;) { for (x = width; x;) { - n = ELEMENTSOF(buf) < x ? ELEMENTSOF(buf) : x; - x -= n; - n *= sizeof(*buf); - while (n) { + x -= n = ELEMENTSOF(buf) < x ? ELEMENTSOF(buf) : x; + for (n *= sizeof(*buf); n; n -= (size_t)r) { r = write(STDOUT_FILENO, buf, n); if (r < 0) eprintf("write <stdout>:"); - n -= (size_t)r; } } } diff --git a/src/vu-stack.c b/src/vu-stack.c @@ -1,113 +1,90 @@ /* See LICENSE file for copyright and license details. */ #include "arg.h" +#include "stream.h" #include "util.h" #include <fcntl.h> #include <inttypes.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> #include <string.h> #include <unistd.h> -struct file -{ - int fd; - unsigned char buf[1024]; - size_t ptr; - const char *file; -}; - -static unsigned int -clip(unsigned int value) -{ - return value < 0 ? 0 : value > 255 ? 255 : value; -} - static void -blend(struct file *files, size_t n_files, size_t n) +process_xyza(struct stream *streams, size_t n_streams, size_t n) { - double r1, g1, b1, a1; - double r2, g2, b2, a2; + double x1, y1, z1, a1; + double x2, y2, z2, a2; size_t i, j; - for (i = 0; i < n; i += 4) { - r1 = srgb_decode(files->buf[i + 0] / 255.); - g1 = srgb_decode(files->buf[i + 1] / 255.); - b1 = srgb_decode(files->buf[i + 2] / 255.); - a1 = files->buf[i + 3] / 255.; - for (j = 1; j < n_files;) { - r2 = srgb_decode(files[j].buf[i + 0] / 255.); - g2 = srgb_decode(files[j].buf[i + 1] / 255.); - b2 = srgb_decode(files[j].buf[i + 2] / 255.); - a2 = files[j].buf[i + 3] / 255.; - a2 /= ++j; - r1 = r1 * a1 * (1 - a2) + r2 * a2; - g1 = g1 * a1 * (1 - a2) + g2 * a2; - b1 = b1 * a1 * (1 - a2) + b2 * a2; + for (i = 0; i < n; i += streams->pixel_size) { + x1 = ((double *)(streams[0].buf + i))[0]; + y1 = ((double *)(streams[0].buf + i))[1]; + z1 = ((double *)(streams[0].buf + i))[2]; + a1 = ((double *)(streams[0].buf + i))[3]; + for (j = 1; j < n_streams; j++) { + x2 = ((double *)(streams[j].buf + i))[0]; + y2 = ((double *)(streams[j].buf + i))[1]; + z2 = ((double *)(streams[j].buf + i))[2]; + a2 = ((double *)(streams[j].buf + i))[3]; + x1 = x1 * a1 * (1 - a2) + x2 * a2; + y1 = y1 * a1 * (1 - a2) + y2 * a2; + z1 = z1 * a1 * (1 - a2) + z2 * a2; a1 = a1 * (1 - a2) + a2; } - r1 = srgb_encode(r1) * 255 + 0.5; - g1 = srgb_encode(g1) * 255 + 0.5; - b1 = srgb_encode(b1) * 255 + 0.5; - a1 = a1 * 255 * 0.5; - files->buf[i + 0] = clip((unsigned int)r1); - files->buf[i + 1] = clip((unsigned int)g1); - files->buf[i + 2] = clip((unsigned int)b1); - files->buf[i + 3] = clip((unsigned int)a1); + ((double *)(streams[0].buf + i))[0] = x1; + ((double *)(streams[0].buf + i))[1] = y1; + ((double *)(streams[0].buf + i))[2] = z1; + ((double *)(streams[0].buf + i))[3] = a1; } } static void -stack(struct file *files, size_t n_files, size_t n) +process_xyza_b(struct stream *streams, size_t n_streams, size_t n) { - double r1, g1, b1, a1; - double r2, g2, b2, a2; + double x1, y1, z1, a1; + double x2, y2, z2, a2; size_t i, j; - for (i = 0; i < n; i += 4) { - r1 = srgb_decode(files->buf[i + 0] / 255.); - g1 = srgb_decode(files->buf[i + 1] / 255.); - b1 = srgb_decode(files->buf[i + 2] / 255.); - a1 = files->buf[i + 3] / 255.; - for (j = 1; j < n_files; j++) { - r2 = srgb_decode(files[j].buf[i + 0] / 255.); - g2 = srgb_decode(files[j].buf[i + 1] / 255.); - b2 = srgb_decode(files[j].buf[i + 2] / 255.); - a2 = files[j].buf[i + 3] / 255.; - r1 = r1 * a1 * (1 - a2) + r2 * a2; - g1 = g1 * a1 * (1 - a2) + g2 * a2; - b1 = b1 * a1 * (1 - a2) + b2 * a2; + for (i = 0; i < n; i += streams->pixel_size) { + x1 = ((double *)(streams[0].buf + i))[0]; + y1 = ((double *)(streams[0].buf + i))[1]; + z1 = ((double *)(streams[0].buf + i))[2]; + a1 = ((double *)(streams[0].buf + i))[3]; + for (j = 1; j < n_streams;) { + x2 = ((double *)(streams[j].buf + i))[0]; + y2 = ((double *)(streams[j].buf + i))[1]; + z2 = ((double *)(streams[j].buf + i))[2]; + a2 = ((double *)(streams[j].buf + i))[3]; + a2 /= ++j; + x1 = x1 * a1 * (1 - a2) + x2 * a2; + y1 = y1 * a1 * (1 - a2) + y2 * a2; + z1 = z1 * a1 * (1 - a2) + z2 * a2; a1 = a1 * (1 - a2) + a2; } - r1 = srgb_encode(r1) * 255 + 0.5; - g1 = srgb_encode(g1) * 255 + 0.5; - b1 = srgb_encode(b1) * 255 + 0.5; - a1 = a1 * 255 * 0.5; - files->buf[i + 0] = clip((unsigned int)r1); - files->buf[i + 1] = clip((unsigned int)g1); - files->buf[i + 2] = clip((unsigned int)b1); - files->buf[i + 3] = clip((unsigned int)a1); + ((double *)(streams[0].buf + i))[0] = x1; + ((double *)(streams[0].buf + i))[1] = y1; + ((double *)(streams[0].buf + i))[2] = z1; + ((double *)(streams[0].buf + i))[3] = a1; } } static void usage(void) { - eprintf("usage: %s [-b] bottom-image ... top-image\n", argv0); + eprintf("usage: %s [-b] bottom-stream ... top-stream\n", argv0); } int main(int argc, char *argv[]) { - struct file *files; - size_t n_files; - int blend_flag = 0; + struct stream *streams; + size_t n_streams; + int blend = 0; size_t i, j, n; ssize_t r; size_t closed; + void (*process)(struct stream *streams, size_t n_streams, size_t n); ARGBEGIN { case 'b': - blend_flag = 1; + blend = 1; break; default: usage(); @@ -116,56 +93,63 @@ main(int argc, char *argv[]) if (argc < 2) usage(); - n_files = (size_t)argc; - files = calloc(n_files, sizeof(*files)); - if (!files) + n_streams = (size_t)argc; + streams = calloc(n_streams, sizeof(*streams)); + if (!streams) eprintf("calloc:"); - for (i = 0; i < n_files; i++) { - files[i].fd = open(argv[i], O_RDONLY); - if (files[i].fd < 0) - eprintf("open %s:", argv[i]); - files[i].file = argv[i]; + for (i = 0; i < n_streams; i++) { + streams[i].file = argv[i]; + streams[i].fd = open(streams[i].file, O_RDONLY); + if (streams[i].fd < 0) + eprintf("open %s:", streams[i].file); + if (i) { + if (streams[i].width != streams->width || streams[i].height != streams->height) + eprintf("videos do not have the same geometry\n"); + if (strcmp(streams[i].pixfmt, streams->pixfmt)) + eprintf("videos use incompatible pixel formats\n"); + } } - while (n_files) { + if (!strcmp(streams->pixfmt, "xyza")) + process = blend ? process_xyza_b : process_xyza; + else + eprintf("pixel format %s is not supported, try xyza\n", streams->pixfmt); + + while (n_streams) { n = SIZE_MAX; - for (i = 0; i < n_files; i++) { - r = read(files[i].fd, files[i].buf + files[i].ptr, sizeof(files[i].buf) - files[i].ptr); - if (r < 0) { - eprintf("read %s:", files[i].file); - } else if (r == 0) { - close(files[i].fd); - files[i].fd = -1; - } else { - files[i].ptr += (size_t)r; + for (i = 0; i < n_streams; i++) { + if (streams[i].ptr < sizeof(streams->buf) && !eread_stream(streams + i, SIZE_MAX)) { + close(streams[i].fd); + streams[i].fd = -1; } - if (files[i].ptr && files[i].ptr < n) - n = files[i].ptr; + if (streams[i].ptr && streams[i].ptr < n) + n = streams[i].ptr; } - n -= n & 3; + n -= n % streams->pixel_size; - (blend_flag ? blend : stack)(files, n_files, n); + process(streams, n_streams, n); for (j = 0; j < n;) { - r = write(STDOUT_FILENO, files->buf + j, n - j); + r = write(STDOUT_FILENO, streams->buf + j, n - j); if (r < 0) eprintf("write <stdout>:"); j += (size_t)r; } closed = SIZE_MAX; - for (i = 0; i < n_files; i++) { - memmove(files[i].buf, files[i].buf + n, files[i].ptr -= n); - if (files[i].ptr < 4 && files[i].fd < 0 && closed == SIZE_MAX) + for (i = 0; i < n_streams; i++) { + memmove(streams[i].buf, streams[i].buf + n, streams[i].ptr -= n); + if (streams[i].ptr < streams->pixel_size && streams[i].fd < 0 && closed == SIZE_MAX) closed = i; } if (closed != SIZE_MAX) { - for (i = (j = closed) + 1; i < n_files; i++) - if (files[i].ptr < 4 && files[i].fd < 0) - files[j++] = files[i]; - n_files = j; + for (i = (j = closed) + 1; i < n_streams; i++) + if (streams[i].ptr < streams->pixel_size && streams[i].fd < 0) + streams[j++] = streams[i]; + n_streams = j; } } + free(streams); return 0; } diff --git a/src/vu-write-head.c b/src/vu-write-head.c @@ -0,0 +1,31 @@ +/* See LICENSE file for copyright and license details. */ +#include "arg.h" +#include "util.h" + +static void +usage(void) +{ + eprintf("usage: %s parameters ...\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + int i; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (!argc) + usage(); + + printf("%s", argv[0]); + for (i = 0; i < argc; i++) + printf(" %s", argv[i]); + printf("\n%cuivf", 0); + + efshut(stdout, "<stdout>"); + return 0; +}