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:
| M | README | | | 10 | ---------- |
| A | src/stream.c | | | 138 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/stream.h | | | 28 | ++++++++++++++++++++++++++++ |
| M | src/util.h | | | 42 | +++++++++++++++++++++++++++++++++++++++++- |
| A | src/vu-colour-srgb.c | | | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/vu-concat.c | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/vu-dissolve.c | | | 109 | ++++++++++++++++++++++++++++++++++++++----------------------------------------- |
| M | src/vu-frame-to-image.c | | | 138 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
| M | src/vu-image-to-frame.c | | | 98 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------- |
| M | src/vu-next-frame.c | | | 75 | ++++++++++++++++++++++++++++++++++++++++++++++----------------------------- |
| A | src/vu-read-head.c | | | 78 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/vu-repeat.c | | | 27 | +++++++++++++++++++-------- |
| M | src/vu-set-alpha.c | | | 154 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
| M | src/vu-single-colour.c | | | 57 | +++++++++++++++++++++++++++++---------------------------- |
| M | src/vu-stack.c | | | 186 | ++++++++++++++++++++++++++++++++++++------------------------------------------- |
| A | src/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;
+}