Add gradients - 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
       ---
 (DIR) commit e76229ac4f95ac8c6e4e38e60689a09a091e4048
 (DIR) parent c91c5cb8e892f1c19c970cef033cc03c9f47f98a
 (HTM) Author: Mattias Andrée <maandree@kth.se>
       Date:   Sun,  2 Jul 2017 23:58:01 +0200
       
       Add gradients
       
       Signed-off-by: Mattias Andrée <maandree@kth.se>
       
       Diffstat:
         M Makefile                            |      12 ++++++++++++
         A src/blind-cone-gradient.c           |     126 +++++++++++++++++++++++++++++++
         A src/blind-coordinate-field.c        |      77 +++++++++++++++++++++++++++++++
         A src/blind-double-sinus-wave.c       |      94 +++++++++++++++++++++++++++++++
         A src/blind-linear-gradient.c         |     101 +++++++++++++++++++++++++++++++
         A src/blind-radial-gradient.c         |     128 +++++++++++++++++++++++++++++++
         A src/blind-round-wave.c              |      89 +++++++++++++++++++++++++++++++
         A src/blind-sawtooth-wave.c           |      84 +++++++++++++++++++++++++++++++
         A src/blind-sinc-wave.c               |     114 +++++++++++++++++++++++++++++++
         A src/blind-sinus-wave.c              |      94 +++++++++++++++++++++++++++++++
         A src/blind-spiral-gradient.c         |     134 +++++++++++++++++++++++++++++++
         A src/blind-square-gradient.c         |     109 +++++++++++++++++++++++++++++++
         A src/blind-triangular-wave.c         |     101 +++++++++++++++++++++++++++++++
         M src/video-math.h                    |       4 ++++
       
       14 files changed, 1267 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -9,7 +9,9 @@ BIN =\
                blind-colour-srgb\
                blind-compress\
                blind-concat\
       +        blind-cone-gradient\
                blind-convert\
       +        blind-coordinate-field\
                blind-crop\
                blind-cross-product\
                blind-cut\
       @@ -17,6 +19,7 @@ BIN =\
                blind-disperse\
                blind-dissolve\
                blind-dot-product\
       +        blind-double-sinus-wave\
                blind-extend\
                blind-find-rectangle\
                blind-flip\
       @@ -29,26 +32,35 @@ BIN =\
                blind-gauss-blur\
                blind-interleave\
                blind-invert-luma\
       +        blind-linear-gradient\
                blind-make-kernel\
                blind-next-frame\
                blind-norm\
                blind-quaternion-product\
                blind-premultiply\
       +        blind-radial-gradient\
                blind-read-head\
                blind-repeat\
                blind-reverse\
                blind-rewrite-head\
       +        blind-round-wave\
       +        blind-sawtooth-wave\
                blind-set-alpha\
                blind-set-luma\
                blind-set-saturation\
                blind-single-colour\
       +        blind-sinc-wave\
       +        blind-sinus-wave\
                blind-skip-pattern\
       +        blind-spiral-gradient\
                blind-split\
                blind-split-cols\
                blind-split-rows\
       +        blind-square-gradient\
                blind-stack\
                blind-tee\
                blind-time-blur\
       +        blind-triangular-wave\
                blind-to-image\
                blind-to-named\
                blind-to-portable\
 (DIR) diff --git a/src/blind-cone-gradient.c b/src/blind-cone-gradient.c
       @@ -0,0 +1,126 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-a | -s] -w width -h height")
       +
       +static int anticlockwise = 0;
       +static int symmetric = 0;
       +static size_t width = 0;
       +static size_t height = 0;
       +static int with_multiplier = 0;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t buf[BUFSIZ / sizeof(pixel_t)];\
       +                TYPE *params, x1, y1, x2, y2;\
       +                TYPE x, y, u, v, m = 1;\
       +                size_t ix, iy, ptr = 0;\
       +                for (;;) {\
       +                        while (stream->ptr < stream->frame_size) {\
       +                                if (!eread_stream(stream, stream->frame_size - stream->ptr)) {\
       +                                        ewriteall(STDOUT_FILENO, buf, ptr * sizeof(*buf), "<stdout>");\
       +                                        return;\
       +                                }\
       +                        }\
       +                        params = (TYPE *)stream->buf;\
       +                        x1 = (params)[0];\
       +                        y1 = (params)[1];\
       +                        x2 = (params)[4];\
       +                        y2 = (params)[5];\
       +                        if (with_multiplier)\
       +                                m = (params)[9];\
       +                        memmove(stream->buf, stream->buf + stream->frame_size,\
       +                                stream->ptr -= stream->frame_size);\
       +                        \
       +                        x2 -= x1;\
       +                        y2 -= y1;\
       +                        u = atan2(y2, x2);\
       +                        \
       +                        for (iy = 0; iy < height; iy++) {\
       +                                y = (TYPE)iy - y1;\
       +                                for (ix = 0; ix < width; ix++) {\
       +                                        x = (TYPE)ix - x1;\
       +                                        v = atan2(y, x);\
       +                                        v -= u;\
       +                                        v += 2 * (TYPE)M_PI;\
       +                                        v = mod(v, 2 * (TYPE)M_PI);\
       +                                        v /= 2 * (TYPE)M_PI;\
       +                                        if (anticlockwise)\
       +                                                v = 1 - v;\
       +                                        v *= m;\
       +                                        if (symmetric) {\
       +                                                v = mod(2 * v, (TYPE)2);\
       +                                                if (v > 1)\
       +                                                        v = 2 - v;\
       +                                        }\
       +                                        buf[ptr][0] = buf[ptr][1] = buf[ptr][2] = buf[ptr][3] = v;\
       +                                        if (++ptr == ELEMENTSOF(buf)) {\
       +                                                ewriteall(STDOUT_FILENO, buf, sizeof(buf), "<stdout>");\
       +                                                ptr = 0;\
       +                                        }\
       +                                }\
       +                        }\
       +                }\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'a':
       +                if (symmetric)
       +                        usage();
       +                anticlockwise = 1;
       +                break;
       +        case 's':
       +                if (anticlockwise)
       +                        usage();
       +                symmetric = 1;
       +                break;
       +        case 'w':
       +                width = etozu_flag('w', UARGF(), 1, SIZE_MAX);
       +                break;
       +        case 'h':
       +                height = etozu_flag('h', UARGF(), 1, SIZE_MAX);
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (!width || !height || argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        if (stream.width > 3 || stream.height > 3 ||
       +            stream.width * stream.height < 2 ||
       +            stream.width * stream.height > 3)
       +                eprintf("<stdin>: each frame must contain exactly 2 or 3 pixels\n");
       +
       +        with_multiplier = stream.width * stream.height == 3;
       +
       +        stream.width = width;
       +        stream.height = height;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-coordinate-field.c b/src/blind-coordinate-field.c
       @@ -0,0 +1,77 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-f frames | -f 'inf'] [-F pixel-format] -w width -h height")
       +
       +static struct stream stream = { .width = 0, .height = 0, .frames = 1 };
       +static int inf = 0;
       +
       +#define PROCESS(TYPE)\
       +        do {\
       +                TYPE buf[4] = {0, 0, 0, 0};\
       +                size_t x, y;\
       +                \
       +                while (inf || stream.frames--) {\
       +                        for (y = 0; y < stream.height; y++) {\
       +                                buf[1] = (TYPE)y;\
       +                                for (x = 0; x < stream.width; x++) {\
       +                                        buf[0] = (TYPE)x;\
       +                                        if (write(STDOUT_FILENO, buf, sizeof(buf)) < 0)\
       +                                                eprintf("write <stdout>:");\
       +                                }\
       +                        }\
       +                }\
       +        } while (0)
       +
       +static void process_lf(void) {PROCESS(double);}
       +static void process_f(void) {PROCESS(float);}
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        char *arg;
       +        const char *pixfmt = "xyza";
       +        void (*process)(void);
       +
       +        ARGBEGIN {
       +        case 'f':
       +                arg = UARGF();
       +                if (!strcmp(arg, "inf"))
       +                        inf = 1, stream.frames = 0;
       +                else
       +                        stream.frames = etozu_flag('f', arg, 1, SIZE_MAX);
       +                break;
       +        case 'F':
       +                pixfmt = UARGF();
       +                break;
       +        case 'w':
       +                stream.width = etozu_flag('w', UARGF(), 1, SIZE_MAX);
       +                break;
       +        case 'h':
       +                stream.height = etozu_flag('h', UARGF(), 1, SIZE_MAX);
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (!stream.width || !stream.height || argc)
       +                usage();
       +
       +        if (inf)
       +                einf_check_fd(STDOUT_FILENO, "<stdout>");
       +
       +        pixfmt = get_pixel_format(pixfmt, "xyza");
       +        if (!strcmp(pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", pixfmt);
       +
       +        strcpy(stream.pixfmt, pixfmt);
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +
       +        process();
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-double-sinus-wave.c b/src/blind-double-sinus-wave.c
       @@ -0,0 +1,94 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-e]")
       +
       +static int equal = 0;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                size_t i, n;\
       +                TYPE x, y, z, a;\
       +                do {\
       +                        n = stream->ptr / stream->pixel_size;\
       +                        if (equal) {\
       +                                for (i = 0; i < n; i++) {\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        a = posmod(a, (TYPE)2);\
       +                                        a = a > 1 ? 2 - a : a;\
       +                                        a = 1 - (sin(a * (2 * (TYPE)M_PI)) + 1) / 2;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        } else {\
       +                                for (i = 0; i < n; i++) {\
       +                                        x = ((TYPE *)(stream->buf))[4 * i + 0];\
       +                                        y = ((TYPE *)(stream->buf))[4 * i + 1];\
       +                                        z = ((TYPE *)(stream->buf))[4 * i + 2];\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        x = posmod(x, (TYPE)1);\
       +                                        y = posmod(y, (TYPE)1);\
       +                                        z = posmod(z, (TYPE)1);\
       +                                        a = posmod(a, (TYPE)1);\
       +                                        x = x > 1 ? 2 - x : x;\
       +                                        y = y > 1 ? 2 - y : y;\
       +                                        z = z > 1 ? 2 - z : z;\
       +                                        a = a > 1 ? 2 - a : a;\
       +                                        x = 1 - (sin(x * (2 * (TYPE)M_PI)) + 1) / 2;\
       +                                        y = 1 - (sin(y * (2 * (TYPE)M_PI)) + 1) / 2;\
       +                                        z = 1 - (sin(z * (2 * (TYPE)M_PI)) + 1) / 2;\
       +                                        a = 1 - (sin(a * (2 * (TYPE)M_PI)) + 1) / 2;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = x;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = y;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = z;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        }\
       +                        n *= stream->pixel_size;\
       +                        ewriteall(STDOUT_FILENO, stream->buf, n, "<stdout>");\
       +                        memmove(stream->buf, stream->buf + n, stream->ptr -= n);\
       +                } while (eread_stream(stream, SIZE_MAX));\
       +                if (stream->ptr)\
       +                        eprintf("%s: incomplete frame\n", stream->file);\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'e':
       +                equal = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-linear-gradient.c b/src/blind-linear-gradient.c
       @@ -0,0 +1,101 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-b] -w width -h height")
       +
       +static int bilinear = 0;
       +static size_t width = 0;
       +static size_t height = 0;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t buf[BUFSIZ / sizeof(pixel_t)];\
       +                TYPE *params, x1, y1, x2, y2, norm2;\
       +                TYPE x, y;\
       +                size_t ix, iy, ptr = 0;\
       +                for (;;) {\
       +                        while (stream->ptr < stream->frame_size) {\
       +                                if (!eread_stream(stream, stream->frame_size - stream->ptr)) {\
       +                                        ewriteall(STDOUT_FILENO, buf, ptr * sizeof(*buf), "<stdout>");\
       +                                        return;\
       +                                }\
       +                        }\
       +                        params = (TYPE *)stream->buf;\
       +                        x1 = (params)[0];\
       +                        y1 = (params)[1];\
       +                        x2 = (params)[4];\
       +                        y2 = (params)[5];\
       +                        memmove(stream->buf, stream->buf + stream->frame_size,\
       +                                stream->ptr -= stream->frame_size);\
       +                        \
       +                        x2 -= x1;\
       +                        y2 -= y1;\
       +                        norm2 = x2 * x2 + y2 * y2;\
       +                        \
       +                        for (iy = 0; iy < height; iy++) {\
       +                                y = (TYPE)iy - y1;\
       +                                for (ix = 0; ix < width; ix++) {\
       +                                        x = (TYPE)ix - x1;\
       +                                        x = (x * x2 + y * y2) / norm2;\
       +                                        if (bilinear)\
       +                                                x = abs(x);\
       +                                        buf[ptr][0] = buf[ptr][1] = buf[ptr][2] = buf[ptr][3] = x;\
       +                                        if (++ptr == ELEMENTSOF(buf)) {\
       +                                                ewriteall(STDOUT_FILENO, buf, sizeof(buf), "<stdout>");\
       +                                                ptr = 0;\
       +                                        }\
       +                                }\
       +                        }\
       +                }\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'b':
       +                bilinear = 1;
       +                break;
       +        case 'w':
       +                width = etozu_flag('w', UARGF(), 1, SIZE_MAX);
       +                break;
       +        case 'h':
       +                height = etozu_flag('h', UARGF(), 1, SIZE_MAX);
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (!width || !height || argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        if (stream.width > 2 || stream.height > 2 || stream.width * stream.height != 2)
       +                eprintf("<stdin>: each frame must contain exactly 2 pixels\n");
       +
       +        stream.width = width;
       +        stream.height = height;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-radial-gradient.c b/src/blind-radial-gradient.c
       @@ -0,0 +1,128 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("-w width -h height")
       +
       +static size_t width = 0;
       +static size_t height = 0;
       +static int with_params;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t buf[BUFSIZ / sizeof(pixel_t)];\
       +                TYPE *params, x1, y1, x2, y2, norm, rd = 1, pe = 2, re = 2, e = 0.5;\
       +                TYPE x, y, p, r, rx, ry;\
       +                size_t ix, iy, ptr = 0;\
       +                for (;;) {\
       +                        while (stream->ptr < stream->frame_size) {\
       +                                if (!eread_stream(stream, stream->frame_size - stream->ptr)) {\
       +                                        ewriteall(STDOUT_FILENO, buf, ptr * sizeof(*buf), "<stdout>");\
       +                                        return;\
       +                                }\
       +                        }\
       +                        params = (TYPE *)stream->buf;\
       +                        x1 = (params)[0];\
       +                        y1 = (params)[1];\
       +                        x2 = (params)[4];\
       +                        y2 = (params)[5];\
       +                        if (with_params) {\
       +                                pe = (params)[8];\
       +                                re = (params)[9];\
       +                                rd = (params)[10];\
       +                                e = 1 / sqrt(pe * re);\
       +                        }\
       +                        memmove(stream->buf, stream->buf + stream->frame_size,\
       +                                stream->ptr -= stream->frame_size);\
       +                        \
       +                        x2 -= x1;\
       +                        y2 -= y1;\
       +                        norm = sqrt(x2 * x2 + y2 * y2);\
       +                        \
       +                        if (!with_params) {\
       +                                for (iy = 0; iy < height; iy++) {\
       +                                        y = (TYPE)iy - y1;\
       +                                        y *= y;\
       +                                        for (ix = 0; ix < width; ix++) {\
       +                                                x = (TYPE)ix - x1;\
       +                                                x = sqrt(x * x + y) / norm;\
       +                                                buf[ptr][0] = buf[ptr][1] = buf[ptr][2] = buf[ptr][3] = x;\
       +                                                if (++ptr == ELEMENTSOF(buf)) {\
       +                                                        ewriteall(STDOUT_FILENO, buf, sizeof(buf), "<stdout>");\
       +                                                        ptr = 0;\
       +                                                }\
       +                                        }\
       +                                }\
       +                        } else {\
       +                                for (iy = 0; iy < height; iy++) {\
       +                                        y = (TYPE)iy - y1;\
       +                                        for (ix = 0; ix < width; ix++) {\
       +                                                x = (TYPE)ix - x1;\
       +                                                p = (x * x2 + y * y2) / norm;\
       +                                                rx = x - p * x2 / norm;\
       +                                                ry = y - p * y2 / norm;\
       +                                                r = sqrt(rx * rx + ry * ry) / rd;\
       +                                                p = pow(abs(p / norm), pe);\
       +                                                r = pow(abs(r / norm), re);\
       +                                                x = pow(p + r, e);\
       +                                                buf[ptr][0] = buf[ptr][1] = buf[ptr][2] = buf[ptr][3] = x;\
       +                                                if (++ptr == ELEMENTSOF(buf)) {\
       +                                                        ewriteall(STDOUT_FILENO, buf, sizeof(buf), "<stdout>");\
       +                                                        ptr = 0;\
       +                                                }\
       +                                        }\
       +                                }\
       +                        }\
       +                }\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'w':
       +                width = etozu_flag('w', UARGF(), 1, SIZE_MAX);
       +                break;
       +        case 'h':
       +                height = etozu_flag('h', UARGF(), 1, SIZE_MAX);
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (!width || !height || argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        if (stream.width > 3 || stream.height > 3 ||
       +            stream.width * stream.height < 2 ||
       +            stream.width * stream.height > 3)
       +                eprintf("<stdin>: each frame must contain exactly 2 pixels\n");
       +
       +        with_params = stream.width * stream.height == 3;
       +
       +        stream.width = width;
       +        stream.height = height;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-round-wave.c b/src/blind-round-wave.c
       @@ -0,0 +1,89 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-e]")
       +
       +static int equal = 0;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                size_t i, n;\
       +                TYPE x, y, z, a;\
       +                do {\
       +                        n = stream->ptr / stream->pixel_size;\
       +                        if (equal) {\
       +                                for (i = 0; i < n; i++) {\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        a = mod(a + 1, (TYPE)4) - 1;\
       +                                        a = a < 1 ? 1 - a * a / 2 : (a - 2) * (a - 2) / 2;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        } else {\
       +                                for (i = 0; i < n; i++) {\
       +                                        x = ((TYPE *)(stream->buf))[4 * i + 0];\
       +                                        y = ((TYPE *)(stream->buf))[4 * i + 1];\
       +                                        z = ((TYPE *)(stream->buf))[4 * i + 2];\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        x = mod(x + 1, (TYPE)4) - 1;\
       +                                        y = mod(y + 1, (TYPE)4) - 1;\
       +                                        z = mod(z + 1, (TYPE)4) - 1;\
       +                                        a = mod(a + 1, (TYPE)4) - 1;\
       +                                        x = x < 1 ? 1 - x * x / 2 : (x - 2) * (x - 2) / 2;\
       +                                        y = y < 1 ? 1 - y * y / 2 : (y - 2) * (y - 2) / 2;\
       +                                        z = z < 1 ? 1 - z * z / 2 : (z - 2) * (z - 2) / 2;\
       +                                        a = a < 1 ? 1 - a * a / 2 : (a - 2) * (a - 2) / 2;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = x;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = y;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = z;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        }\
       +                        n *= stream->pixel_size;\
       +                        ewriteall(STDOUT_FILENO, stream->buf, n, "<stdout>");\
       +                        memmove(stream->buf, stream->buf + n, stream->ptr -= n);\
       +                } while (eread_stream(stream, SIZE_MAX));\
       +                if (stream->ptr)\
       +                        eprintf("%s: incomplete frame\n", stream->file);\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'e':
       +                equal = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-sawtooth-wave.c b/src/blind-sawtooth-wave.c
       @@ -0,0 +1,84 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-e]")
       +
       +static int equal = 0;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                size_t i, n;\
       +                TYPE x, y, z, a;\
       +                do {\
       +                        n = stream->ptr / stream->pixel_size;\
       +                        if (equal) {\
       +                                for (i = 0; i < n; i++) {\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        a = posmod(a, (TYPE)1);\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        } else {\
       +                                for (i = 0; i < n; i++) {\
       +                                        x = ((TYPE *)(stream->buf))[4 * i + 0];\
       +                                        y = ((TYPE *)(stream->buf))[4 * i + 1];\
       +                                        z = ((TYPE *)(stream->buf))[4 * i + 2];\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        x = posmod(x, (TYPE)1);\
       +                                        y = posmod(y, (TYPE)1);\
       +                                        z = posmod(z, (TYPE)1);\
       +                                        a = posmod(a, (TYPE)1);\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = x;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = y;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = z;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        }\
       +                        n *= stream->pixel_size;\
       +                        ewriteall(STDOUT_FILENO, stream->buf, n, "<stdout>");\
       +                        memmove(stream->buf, stream->buf + n, stream->ptr -= n);\
       +                } while (eread_stream(stream, SIZE_MAX));\
       +                if (stream->ptr)\
       +                        eprintf("%s: incomplete frame\n", stream->file);\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'e':
       +                equal = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-sinc-wave.c b/src/blind-sinc-wave.c
       @@ -0,0 +1,114 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-e] [theta0-stream]")
       +
       +static int equal = 0;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *grad, struct stream *theta0)\
       +        {\
       +                size_t i, n, m = 0;\
       +                TYPE *theta0xyza;\
       +                TYPE x, theta0x = 0;\
       +                TYPE y, theta0y = 0;\
       +                TYPE z, theta0z = 0;\
       +                TYPE a, theta0a = 0;\
       +                echeck_dimensions(grad, WIDTH | HEIGHT, NULL);\
       +                do {\
       +                        if (!m) {\
       +                                m = grad->frame_size;\
       +                                if (theta0) {\
       +                                        while (theta0->ptr < theta0->frame_size)\
       +                                                if (!eread_stream(theta0, theta0->frame_size - theta0->ptr))\
       +                                                        return;\
       +                                        theta0xyza = (TYPE *)theta0->buf;\
       +                                        theta0x = (theta0xyza)[0];\
       +                                        theta0y = (theta0xyza)[1];\
       +                                        theta0z = (theta0xyza)[2];\
       +                                        theta0a = (theta0xyza)[3];\
       +                                        memmove(theta0->buf, theta0->buf + theta0->frame_size,\
       +                                                theta0->ptr -= theta0->frame_size);\
       +                                }\
       +                        }\
       +                        n = MIN(grad->ptr, m) / grad->pixel_size;\
       +                        if (equal) {\
       +                                for (i = 0; i < n; i++) {\
       +                                        a = ((TYPE *)(grad->buf))[4 * i + 3];\
       +                                        a = (a ? sin(a + theta0y) / a : sin(a + theta0y)) / 2 + 0.5;\
       +                                        ((TYPE *)(grad->buf))[4 * i + 0] = a;\
       +                                        ((TYPE *)(grad->buf))[4 * i + 1] = a;\
       +                                        ((TYPE *)(grad->buf))[4 * i + 2] = a;\
       +                                        ((TYPE *)(grad->buf))[4 * i + 3] = a;\
       +                                }\
       +                        } else {\
       +                                for (i = 0; i < n; i++) {\
       +                                        x = ((TYPE *)(grad->buf))[4 * i + 0];\
       +                                        y = ((TYPE *)(grad->buf))[4 * i + 1];\
       +                                        z = ((TYPE *)(grad->buf))[4 * i + 2];\
       +                                        a = ((TYPE *)(grad->buf))[4 * i + 3];\
       +                                        x = (x ? sin(x + theta0x) / x : sin(x + theta0x)) / 2 + 0.5;\
       +                                        y = (y ? sin(y + theta0y) / y : sin(y + theta0y)) / 2 + 0.5;\
       +                                        z = (z ? sin(z + theta0z) / z : sin(z + theta0z)) / 2 + 0.5;\
       +                                        a = (a ? sin(a + theta0a) / a : sin(a + theta0a)) / 2 + 0.5;\
       +                                        ((TYPE *)(grad->buf))[4 * i + 0] = x;\
       +                                        ((TYPE *)(grad->buf))[4 * i + 1] = y;\
       +                                        ((TYPE *)(grad->buf))[4 * i + 2] = z;\
       +                                        ((TYPE *)(grad->buf))[4 * i + 3] = a;\
       +                                }\
       +                        }\
       +                        n *= grad->pixel_size;\
       +                        m -= n;\
       +                        ewriteall(STDOUT_FILENO, grad->buf, n, "<stdout>");\
       +                        memmove(grad->buf, grad->buf + n, grad->ptr -= n);\
       +                } while (eread_stream(grad, SIZE_MAX));\
       +                if (grad->ptr)\
       +                        eprintf("%s: incomplete frame\n", grad->file);\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream, theta0;
       +        int have_theta0;
       +        void (*process)(struct stream *grad, struct stream *theta0);
       +
       +        ARGBEGIN {
       +        case 'e':
       +                equal = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc > 1)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +        if ((have_theta0 = argc == 1)) {
       +                eopen_stream(&theta0, argv[0]);
       +                if (theta0.width != 1 || theta0.height != 1)
       +                        eprintf("theta0-stream must be of dimension 1x1\n");
       +        }
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        if (have_theta0 && strcmp(stream.pixfmt, theta0.pixfmt))
       +                eprintf("videos use incompatible pixel formats\n");
       +
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream, have_theta0 ? &theta0 : NULL);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-sinus-wave.c b/src/blind-sinus-wave.c
       @@ -0,0 +1,94 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-e]")
       +
       +static int equal = 0;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                size_t i, n;\
       +                TYPE x, y, z, a;\
       +                do {\
       +                        n = stream->ptr / stream->pixel_size;\
       +                        if (equal) {\
       +                                for (i = 0; i < n; i++) {\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        a = posmod(a, (TYPE)2);\
       +                                        a = a > 1 ? 2 - a : a;\
       +                                        a = 1 - (cos(a * (TYPE)M_PI) + 1) / 2;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        } else {\
       +                                for (i = 0; i < n; i++) {\
       +                                        x = ((TYPE *)(stream->buf))[4 * i + 0];\
       +                                        y = ((TYPE *)(stream->buf))[4 * i + 1];\
       +                                        z = ((TYPE *)(stream->buf))[4 * i + 2];\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        x = posmod(x, (TYPE)1);\
       +                                        y = posmod(y, (TYPE)1);\
       +                                        z = posmod(z, (TYPE)1);\
       +                                        a = posmod(a, (TYPE)1);\
       +                                        x = x > 1 ? 2 - x : x;\
       +                                        y = y > 1 ? 2 - y : y;\
       +                                        z = z > 1 ? 2 - z : z;\
       +                                        a = a > 1 ? 2 - a : a;\
       +                                        x = 1 - (cos(x * (TYPE)M_PI) + 1) / 2;\
       +                                        y = 1 - (cos(y * (TYPE)M_PI) + 1) / 2;\
       +                                        z = 1 - (cos(z * (TYPE)M_PI) + 1) / 2;\
       +                                        a = 1 - (cos(a * (TYPE)M_PI) + 1) / 2;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = x;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = y;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = z;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        }\
       +                        n *= stream->pixel_size;\
       +                        ewriteall(STDOUT_FILENO, stream->buf, n, "<stdout>");\
       +                        memmove(stream->buf, stream->buf + n, stream->ptr -= n);\
       +                } while (eread_stream(stream, SIZE_MAX));\
       +                if (stream->ptr)\
       +                        eprintf("%s: incomplete frame\n", stream->file);\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'e':
       +                equal = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-spiral-gradient.c b/src/blind-spiral-gradient.c
       @@ -0,0 +1,134 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-al] -w width -h height")
       +
       +static int anticlockwise = 0;
       +static int logarithmic = 0;
       +static size_t width = 0;
       +static size_t height = 0;
       +static int with_params;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t buf[BUFSIZ / sizeof(pixel_t)];\
       +                TYPE *params, x1, y1, x2, y2, b, r, u, v;\
       +                TYPE x, y, a = 0, e = 1, p = 1, k = 1, ep = 1;\
       +                size_t ix, iy, ptr = 0;\
       +                for (;;) {\
       +                        while (stream->ptr < stream->frame_size) {\
       +                                if (!eread_stream(stream, stream->frame_size - stream->ptr)) {\
       +                                        ewriteall(STDOUT_FILENO, buf, ptr * sizeof(*buf), "<stdout>");\
       +                                        return;\
       +                                }\
       +                        }\
       +                        params = (TYPE *)stream->buf;\
       +                        x1 = (params)[0];\
       +                        y1 = (params)[1];\
       +                        x2 = (params)[4];\
       +                        y2 = (params)[5];\
       +                        if (with_params) {\
       +                                a = (params)[8];\
       +                                e = (params)[9];\
       +                                p = (params)[10];\
       +                                k = (params)[11];\
       +                                ep = 1 / (e * p);\
       +                        }\
       +                        memmove(stream->buf, stream->buf + stream->frame_size,\
       +                                stream->ptr -= stream->frame_size);\
       +                        \
       +                        x2 -= x1;\
       +                        y2 -= y1;\
       +                        u = atan2(y2, x2);\
       +                        b = sqrt(x2 * x2 + y2 * y2);\
       +                        if (logarithmic)\
       +                                b = log(b);\
       +                        b /= pow(2 * (TYPE)M_PI, e);\
       +                        \
       +                        for (iy = 0; iy < height; iy++) {\
       +                                y = (TYPE)iy - y1;\
       +                                for (ix = 0; ix < width; ix++) {\
       +                                        x = (TYPE)ix - x1;\
       +                                        v = atan2(y, x);\
       +                                        if (anticlockwise)\
       +                                                v = 1 - v;\
       +                                        v -= u;\
       +                                        v += 4 * (TYPE)M_PI;\
       +                                        v = mod(v, 2 * (TYPE)M_PI);\
       +                                        r = sqrt(x * x + y * y);\
       +                                        r -= a;\
       +                                        if (!logarithmic) {\
       +                                                r = pow(r / b, ep);\
       +                                                r = (r - v) / (2 * (TYPE)M_PI);\
       +                                        } else if (r) {\
       +                                                r = log(r / k);\
       +                                                r = pow(r / b, ep);\
       +                                                r = (r - v) / (2 * (TYPE)M_PI);\
       +                                        }\
       +                                        buf[ptr][0] = buf[ptr][1] = buf[ptr][2] = buf[ptr][3] = r;\
       +                                        if (++ptr == ELEMENTSOF(buf)) {\
       +                                                ewriteall(STDOUT_FILENO, buf, sizeof(buf), "<stdout>");\
       +                                                ptr = 0;\
       +                                        }\
       +                                }\
       +                        }\
       +                }\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'a':
       +                anticlockwise = 1;
       +                break;
       +        case 'l':
       +                logarithmic = 1;
       +                break;
       +        case 'w':
       +                width = etozu_flag('w', UARGF(), 1, SIZE_MAX);
       +                break;
       +        case 'h':
       +                height = etozu_flag('h', UARGF(), 1, SIZE_MAX);
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (!width || !height || argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        if (stream.width > 3 || stream.height > 3 ||
       +            stream.width * stream.height < 2 ||
       +            stream.width * stream.height > 3)
       +                eprintf("<stdin>: each frame must contain exactly 2 or 3 pixels\n");
       +
       +        with_params = stream.width * stream.height == 3;
       +
       +        stream.width = width;
       +        stream.height = height;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-square-gradient.c b/src/blind-square-gradient.c
       @@ -0,0 +1,109 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("-w width -h height")
       +
       +static size_t width = 0;
       +static size_t height = 0;
       +static int with_multiplier;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t buf[BUFSIZ / sizeof(pixel_t)];\
       +                TYPE *params, x1, y1, x2, y2, norm, rd = 1; \
       +                TYPE x, y, p, r, rx, ry;\
       +                size_t ix, iy, ptr = 0;\
       +                for (;;) {\
       +                        while (stream->ptr < stream->frame_size) {\
       +                                if (!eread_stream(stream, stream->frame_size - stream->ptr)) {\
       +                                        ewriteall(STDOUT_FILENO, buf, ptr * sizeof(*buf), "<stdout>");\
       +                                        return;\
       +                                }\
       +                        }\
       +                        params = (TYPE *)stream->buf;\
       +                        x1 = (params)[0];\
       +                        y1 = (params)[1];\
       +                        x2 = (params)[4];\
       +                        y2 = (params)[5];\
       +                        if (with_multiplier)\
       +                                rd = (params)[9];\
       +                        memmove(stream->buf, stream->buf + stream->frame_size,\
       +                                stream->ptr -= stream->frame_size);\
       +                        \
       +                        x2 -= x1;\
       +                        y2 -= y1;\
       +                        norm = sqrt(x2 * x2 + y2 * y2);\
       +                        x2 /= norm;\
       +                        y2 /= norm;\
       +                        \
       +                        for (iy = 0; iy < height; iy++) {\
       +                                y = (TYPE)iy - y1;\
       +                                for (ix = 0; ix < width; ix++) {\
       +                                        x = (TYPE)ix - x1;\
       +                                        p = x * x2 + y * y2;\
       +                                        rx = x - p * x2;\
       +                                        ry = y - p * y2;\
       +                                        r = sqrt(rx * rx + ry * ry) / rd;\
       +                                        p = abs(p);\
       +                                        x = MAX(p, r) / norm;\
       +                                        buf[ptr][0] = buf[ptr][1] = buf[ptr][2] = buf[ptr][3] = x;\
       +                                        if (++ptr == ELEMENTSOF(buf)) {\
       +                                                ewriteall(STDOUT_FILENO, buf, sizeof(buf), "<stdout>");\
       +                                                ptr = 0;\
       +                                        }\
       +                                }\
       +                        }\
       +                }\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'w':
       +                width = etozu_flag('w', UARGF(), 1, SIZE_MAX);
       +                break;
       +        case 'h':
       +                height = etozu_flag('h', UARGF(), 1, SIZE_MAX);
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (!width || !height || argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        if (stream.width > 3 || stream.height > 3 ||
       +            stream.width * stream.height < 2 ||
       +            stream.width * stream.height > 3)
       +                eprintf("<stdin>: each frame must contain exactly 2 or 3 pixels\n");
       +
       +        with_multiplier = stream.width * stream.height == 3;
       +
       +        stream.width = width;
       +        stream.height = height;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-triangular-wave.c b/src/blind-triangular-wave.c
       @@ -0,0 +1,101 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-es]")
       +
       +static int equal = 0;
       +static int spiral = 0;
       +
       +
       +#define PROCESS(TYPE, SUFFIX)\
       +        static void\
       +        process_##SUFFIX(struct stream *stream)\
       +        {\
       +                size_t i, n;\
       +                TYPE x, y, z, a;\
       +                do {\
       +                        n = stream->ptr / stream->pixel_size;\
       +                        if (equal) {\
       +                                for (i = 0; i < n; i++) {\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        a = posmod(a, (TYPE)2);\
       +                                        a = a > 1 ? 2 - a : a;\
       +                                        if (spiral)\
       +                                                a = (a > 0.5 ? 1 - a : a) * 2;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = a;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        } else {\
       +                                for (i = 0; i < n; i++) {\
       +                                        x = ((TYPE *)(stream->buf))[4 * i + 0];\
       +                                        y = ((TYPE *)(stream->buf))[4 * i + 1];\
       +                                        z = ((TYPE *)(stream->buf))[4 * i + 2];\
       +                                        a = ((TYPE *)(stream->buf))[4 * i + 3];\
       +                                        x = posmod(x, (TYPE)2);\
       +                                        y = posmod(y, (TYPE)2);\
       +                                        z = posmod(z, (TYPE)2);\
       +                                        a = posmod(a, (TYPE)2);\
       +                                        x = x > 1 ? 2 - x : x;\
       +                                        y = y > 1 ? 2 - y : y;\
       +                                        z = z > 1 ? 2 - z : z;\
       +                                        a = a > 1 ? 2 - a : a;\
       +                                        if (spiral) {\
       +                                                x = (x > 0.5 ? 1 - x : x) * 2;\
       +                                                y = (y > 0.5 ? 1 - y : y) * 2;\
       +                                                z = (z > 0.5 ? 1 - z : z) * 2;\
       +                                                a = (a > 0.5 ? 1 - a : a) * 2;\
       +                                        }\
       +                                        ((TYPE *)(stream->buf))[4 * i + 0] = x;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 1] = y;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 2] = z;\
       +                                        ((TYPE *)(stream->buf))[4 * i + 3] = a;\
       +                                }\
       +                        }\
       +                        n *= stream->pixel_size;\
       +                        ewriteall(STDOUT_FILENO, stream->buf, n, "<stdout>");\
       +                        memmove(stream->buf, stream->buf + n, stream->ptr -= n);\
       +                } while (eread_stream(stream, SIZE_MAX));\
       +                if (stream->ptr)\
       +                        eprintf("%s: incomplete frame\n", stream->file);\
       +        }
       +
       +PROCESS(double, lf)
       +PROCESS(float, f)
       +
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'e':
       +                equal = 1;
       +                break;
       +        case 's':
       +                spiral = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (!strcmp(stream.pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(stream.pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
       +
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/video-math.h b/src/video-math.h
       @@ -59,6 +59,7 @@ posmodf(float a, float b)
        
        #define pow(...)         MATH_GENERIC_N(pow,      __VA_ARGS__)
        #define log2(...)        MATH_GENERIC_1(log2,     __VA_ARGS__)
       +#define log(...)         MATH_GENERIC_1(log,      __VA_ARGS__)
        #define abs(...)         MATH_GENERIC_1(fabs,     __VA_ARGS__)
        #define sqrt(...)        MATH_GENERIC_1(sqrt,     __VA_ARGS__)
        #define exp(...)         MATH_GENERIC_1(exp,      __VA_ARGS__)
       @@ -68,6 +69,9 @@ posmodf(float a, float b)
        #define nnpow(...)       MATH_GENERIC_N(nnpow,    __VA_ARGS__)
        #define mod(...)         MATH_GENERIC_N(fmod,     __VA_ARGS__)
        #define posmod(...)      MATH_GENERIC_N(posmod,   __VA_ARGS__)
       +#define cos(...)         MATH_GENERIC_1(cos,      __VA_ARGS__)
       +#define sin(...)         MATH_GENERIC_1(sin,      __VA_ARGS__)
       +#define atan2(...)       MATH_GENERIC_N(atan2,    __VA_ARGS__)
        
        #define srgb_encode(...) BLIND_GENERIC_1(srgb_encode, __VA_ARGS__)
        #define srgb_decode(...) BLIND_GENERIC_1(srgb_decode, __VA_ARGS__)