Add blind-matrix-{orthoproject,reflect,rotate,scale,shear,translate,transpose} - 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 0ca8f64f61b534ba2428236fec2c8879fe58c059
 (DIR) parent b384c0cc8c69783495e1f21f3fd0f3e6253645b1
 (HTM) Author: Mattias Andrée <maandree@kth.se>
       Date:   Fri, 14 Jul 2017 16:30:30 +0200
       
       Add blind-matrix-{orthoproject,reflect,rotate,scale,shear,translate,transpose}
       
       Signed-off-by: Mattias Andrée <maandree@kth.se>
       
       Diffstat:
         M Makefile                            |       7 +++++++
         M README                              |      21 +++++++++++++++++++++
         A man/blind-matrix-orthoproject.1     |      43 ++++++++++++++++++++++++++++++
         A man/blind-matrix-reflect.1          |      43 ++++++++++++++++++++++++++++++
         A man/blind-matrix-rotate.1           |      45 +++++++++++++++++++++++++++++++
         A man/blind-matrix-scale.1            |      43 ++++++++++++++++++++++++++++++
         A man/blind-matrix-shear.1            |      58 ++++++++++++++++++++++++++++++
         A man/blind-matrix-translate.1        |      47 +++++++++++++++++++++++++++++++
         A man/blind-matrix-transpose.1        |      43 ++++++++++++++++++++++++++++++
         M man/blind.7                         |      21 +++++++++++++++++++++
         A src/blind-matrix-orthoproject.c     |      89 +++++++++++++++++++++++++++++++
         A src/blind-matrix-reflect.c          |      87 +++++++++++++++++++++++++++++++
         A src/blind-matrix-rotate.c           |      79 +++++++++++++++++++++++++++++++
         A src/blind-matrix-scale.c            |      74 +++++++++++++++++++++++++++++++
         A src/blind-matrix-shear.c            |      84 +++++++++++++++++++++++++++++++
         A src/blind-matrix-translate.c        |      74 +++++++++++++++++++++++++++++++
         A src/blind-matrix-transpose.c        |      76 +++++++++++++++++++++++++++++++
         M src/video-math.h                    |       1 +
       
       18 files changed, 935 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -41,6 +41,13 @@ BIN =\
                blind-invert-luma\
                blind-linear-gradient\
                blind-make-kernel\
       +        blind-matrix-orthoproject\
       +        blind-matrix-reflect\
       +        blind-matrix-rotate\
       +        blind-matrix-scale\
       +        blind-matrix-shear\
       +        blind-matrix-translate\
       +        blind-matrix-transpose\
                blind-mosaic\
                blind-mosaic-edges\
                blind-next-frame\
 (DIR) diff --git a/README b/README
       @@ -117,6 +117,27 @@ UTILITIES
               blind-make-kernel(1)
                      Create a custom convolution matrix
        
       +       blind-matrix-orthoproject(1)
       +              Create an affine 2D-transformation matrix for othogonal projection
       +
       +       blind-matrix-reflect(1)
       +              Create an affine 2D-transformation matrix for reflection about a line
       +
       +       blind-matrix-rotate(1)
       +              Create an affine 2D-transformation matrix for rotation
       +
       +       blind-matrix-scale(1)
       +              Create an affine 2D-transformation matrix for scaling
       +
       +       blind-matrix-shear(1)
       +              Create an affine 2D-transformation matrix for shearing
       +
       +       blind-matrix-translate(1)
       +              Create an affine 2D-transformation matrix for translation
       +
       +       blind-matrix-transpose(1)
       +              Create an affine 2D-transformation matrix for transposition
       +
               blind-mosaic(1)
                      Redraw each frame in video as a mosaic
        
 (DIR) diff --git a/man/blind-matrix-orthoproject.1 b/man/blind-matrix-orthoproject.1
       @@ -0,0 +1,43 @@
       +.TH BLIND-MATRIX-ORTHOPROJECT 1 blind
       +.SH NAME
       +blind-matrix-orthoproject - Create an affine 2D-transformation matrix for othogonal projection
       +.SH SYNOPSIS
       +.B blind-matrix-orthoproject
       +[-c]
       +.SH DESCRIPTION
       +.B blind-matrix-orthoproject
       +creates an affine 2D-transformation matrix for
       +othogonal projection. The parameters for the
       +matrix is read in stdin in format of a blind video,
       +one matrix per frame in stdin created and printed
       +to stdout in format of a blind video.
       +.P
       +Each frame in stdin shall contain exactly 2 pixels,
       +the first pixel holds the x-value of the vector the
       +image is projected along, the second pixel holds the
       +y-value of this vector.
       +.P
       +The luma (encoding in the Y-channel, the second
       +channel) multiplied by the alpha (the fourth channel)
       +of the input pixels are used as the values. Each
       +values in the resulting matrices are stored
       +in all channels.
       +.SH OPTIONS
       +.TP
       +.B -c
       +Create different matrices for each channel. Use
       +values from each channel in stdin to create
       +matrices whose values are stored in the same
       +channels in stdout.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-from-text (1),
       +.BR blind-matrix-reflect (1),
       +.BR blind-matrix-rotate (1),
       +.BR blind-matrix-scale (1),
       +.BR blind-matrix-shear (1),
       +.BR blind-matrix-translate (1),
       +.BR blind-matrix-transpose (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
 (DIR) diff --git a/man/blind-matrix-reflect.1 b/man/blind-matrix-reflect.1
       @@ -0,0 +1,43 @@
       +.TH BLIND-MATRIX-REFLECT 1 blind
       +.SH NAME
       +blind-matrix-reflect - Create an affine 2D-transformation matrix for reflection about a line
       +.SH SYNOPSIS
       +.B blind-matrix-reflect
       +[-c]
       +.SH DESCRIPTION
       +.B blind-matrix-reflect
       +creates an affine 2D-transformation matrix for
       +reflection about a line. The parameters for the
       +matrix is read in stdin in format of a blind video,
       +one matrix per frame in stdin created and printed
       +to stdout in format of a blind video.
       +.P
       +Each frame in stdin shall contain exactly 2 pixels,
       +the first pixel holds the x-value of the line going
       +out from origo the image is reflected around, the
       +second pixel holds the y-value of this line.
       +.P
       +The luma (encoding in the Y-channel, the second
       +channel) multiplied by the alpha (the fourth channel)
       +of the input pixels are used as the values. Each
       +values in the resulting matrices are stored
       +in all channels.
       +.SH OPTIONS
       +.TP
       +.B -c
       +Create different matrices for each channel. Use
       +values from each channel in stdin to create
       +matrices whose values are stored in the same
       +channels in stdout.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-from-text (1),
       +.BR blind-matrix-orthoproject (1),
       +.BR blind-matrix-rotate (1),
       +.BR blind-matrix-scale (1),
       +.BR blind-matrix-shear (1),
       +.BR blind-matrix-translate (1),
       +.BR blind-matrix-transpose (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
 (DIR) diff --git a/man/blind-matrix-rotate.1 b/man/blind-matrix-rotate.1
       @@ -0,0 +1,45 @@
       +.TH BLIND-MATRIX-ROTATE 1 blind
       +.SH NAME
       +blind-matrix-rotate - Create an affine 2D-transformation matrix for rotation
       +.SH SYNOPSIS
       +.B blind-matrix-rotate
       +[-c]
       +.SH DESCRIPTION
       +.B blind-matrix-rotate
       +creates an affine 2D-transformation matrix for
       +rotation. The parameters for the matrix is read
       +in stdin in format of a blind video, one matrix
       +per frame in stdin created and printed to stdout
       +in format of a blind video.
       +.P
       +Each frame in stdin shall contain exactly 1 pixel,
       +this pixel holds the number of radians the image
       +is to be rotated clockwise around origo.
       +.P
       +The luma (encoding in the Y-channel, the second
       +channel) multiplied by the alpha (the fourth channel)
       +of the input pixels are used as the values. Each
       +values in the resulting matrices are stored
       +in all channels.
       +.SH OPTIONS
       +.TP
       +.B -c
       +Create different matrices for each channel. Use
       +values from each channel in stdin to create
       +matrices whose values are stored in the same
       +channels in stdout.
       +.SH NOTES
       +The image is rotated anti-clockwise if the Y-axis
       +grows upwards rather than downwards.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-from-text (1),
       +.BR blind-matrix-orthoproject (1),
       +.BR blind-matrix-reflect (1),
       +.BR blind-matrix-scale (1),
       +.BR blind-matrix-shear (1),
       +.BR blind-matrix-translate (1),
       +.BR blind-matrix-transpose (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
 (DIR) diff --git a/man/blind-matrix-scale.1 b/man/blind-matrix-scale.1
       @@ -0,0 +1,43 @@
       +.TH BLIND-MATRIX-SCALE 1 blind
       +.SH NAME
       +blind-matrix-scale - Create an affine 2D-transformation matrix for scaling
       +.SH SYNOPSIS
       +.B blind-matrix-scale
       +[-c]
       +.SH DESCRIPTION
       +.B blind-matrix-scale
       +creates an affine 2D-transformation matrix for
       +scaling. The parameters for the matrix is read
       +in stdin in format of a blind video, one matrix
       +per frame in stdin created and printed to stdout
       +in format of a blind video.
       +.P
       +Each frame in stdin shall contain exactly 2 pixel,
       +the first pixel holds the scale factor for the
       +width and the second pixel holds the scale factor
       +for the height.
       +.P
       +The luma (encoding in the Y-channel, the second
       +channel) multiplied by the alpha (the fourth channel)
       +of the input pixels are used as the values. Each
       +values in the resulting matrices are stored
       +in all channels.
       +.SH OPTIONS
       +.TP
       +.B -c
       +Create different matrices for each channel. Use
       +values from each channel in stdin to create
       +matrices whose values are stored in the same
       +channels in stdout.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-from-text (1),
       +.BR blind-matrix-orthoproject (1),
       +.BR blind-matrix-reflect (1),
       +.BR blind-matrix-rotate (1),
       +.BR blind-matrix-shear (1),
       +.BR blind-matrix-translate (1),
       +.BR blind-matrix-transpose (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
 (DIR) diff --git a/man/blind-matrix-shear.1 b/man/blind-matrix-shear.1
       @@ -0,0 +1,58 @@
       +.TH BLIND-MATRIX-SHEAR 1 blind
       +.SH NAME
       +blind-matrix-shear - Create an affine 2D-transformation matrix for shearing
       +.SH SYNOPSIS
       +.B blind-matrix-shear
       +[-ac]
       +.SH DESCRIPTION
       +.B blind-matrix-shear
       +creates an affine 2D-transformation matrix for
       +shearing. The parameters for the matrix is read
       +in stdin in format of a blind video, one matrix
       +per frame in stdin created and printed to stdout
       +in format of a blind video.
       +.P
       +Each frame in stdin shall contain exactly 2 pixel.
       +The first pixel holds the amount the image shall
       +be sheared horizontally, that is, how much all
       +pixels 1 pixel above the X-axis shall be moved
       +rightward. The second pixel holds the amount the
       +image shall be sheared vertically, that is, how
       +much all pixels 1 pixel right of the Y-axis shall
       +be moved downward.
       +.P
       +The luma (encoding in the Y-channel, the second
       +channel) multiplied by the alpha (the fourth channel)
       +of the input pixels are used as the values. Each
       +values in the resulting matrices are stored
       +in all channels.
       +.SH OPTIONS
       +.TP
       +.B -a
       +The pixels holds the angles of the shearing rather
       +than the amount of the shearing.
       +.TP
       +.B -c
       +Create different matrices for each channel. Use
       +values from each channel in stdin to create
       +matrices whose values are stored in the same
       +channels in stdout.
       +.SH NOTES
       +The description assumes the Y-axis grows downwards.
       +.P
       +Horizontal shearing and vertical shearing is not
       +mutually commutative, this tool performs the
       +shearing at the same time rather than after each
       +other.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-from-text (1),
       +.BR blind-matrix-orthoproject (1),
       +.BR blind-matrix-reflect (1),
       +.BR blind-matrix-rotate (1),
       +.BR blind-matrix-scale (1),
       +.BR blind-matrix-translate (1),
       +.BR blind-matrix-transpose (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
 (DIR) diff --git a/man/blind-matrix-translate.1 b/man/blind-matrix-translate.1
       @@ -0,0 +1,47 @@
       +.TH BLIND-MATRIX-TRANSLATE 1 blind
       +.SH NAME
       +blind-matrix-translate - Create an affine 2D-transformation matrix for translation
       +.SH SYNOPSIS
       +.B blind-matrix-translate
       +[-c]
       +.SH DESCRIPTION
       +.B blind-matrix-translate
       +creates an affine 2D-transformation matrix for
       +translation. The parameters for the matrix is read
       +in stdin in format of a blind video, one matrix
       +per frame in stdin created and printed to stdout
       +in format of a blind video.
       +.P
       +Each frame in stdin shall contain exactly 2 pixel,
       +the first pixel holds the number of pixels the
       +image shall be translated rightwards, and the
       +second pixel holds the number of pixels the
       +image shall be translated downwards.
       +.P
       +The luma (encoding in the Y-channel, the second
       +channel) multiplied by the alpha (the fourth channel)
       +of the input pixels are used as the values. Each
       +values in the resulting matrices are stored
       +in all channels.
       +.SH OPTIONS
       +.TP
       +.B -c
       +Create different matrices for each channel. Use
       +values from each channel in stdin to create
       +matrices whose values are stored in the same
       +channels in stdout.
       +.SH NOTES
       +The image upwards rather than downwards if the
       +Y-axis grows upwards rather than downwards.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-from-text (1),
       +.BR blind-matrix-orthoproject (1),
       +.BR blind-matrix-reflect (1),
       +.BR blind-matrix-rotate (1),
       +.BR blind-matrix-scale (1),
       +.BR blind-matrix-shear (1),
       +.BR blind-matrix-transpose (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
 (DIR) diff --git a/man/blind-matrix-transpose.1 b/man/blind-matrix-transpose.1
       @@ -0,0 +1,43 @@
       +.TH BLIND-MATRIX-TRANSPOSE 1 blind
       +.SH NAME
       +blind-matrix-transpose - Create an affine 2D-transformation matrix for transposition
       +.SH SYNOPSIS
       +.B blind-matrix-transpose
       +[-c]
       +.SH DESCRIPTION
       +.B blind-matrix-transpose
       +creates an affine 2D-transformation matrix for
       +transposition. The parameters for the matrix is read
       +in stdin in format of a blind video, one matrix
       +per frame in stdin created and printed to stdout
       +in format of a blind video.
       +.P
       +Each frame in stdin shall contain exactly 1 pixel,
       +this pixel holds degree of the transposition, 0
       +meaning no transposition at all, 1 meaning full
       +transposition.
       +.P
       +The luma (encoding in the Y-channel, the second
       +channel) multiplied by the alpha (the fourth channel)
       +of the input pixels are used as the values. Each
       +values in the resulting matrices are stored
       +in all channels.
       +.SH OPTIONS
       +.TP
       +.B -c
       +Create different matrices for each channel. Use
       +values from each channel in stdin to create
       +matrices whose values are stored in the same
       +channels in stdout.
       +.SH SEE ALSO
       +.BR blind (7),
       +.BR blind-from-text (1),
       +.BR blind-matrix-orthoproject (1),
       +.BR blind-matrix-reflect (1),
       +.BR blind-matrix-rotate (1),
       +.BR blind-matrix-scale (1),
       +.BR blind-matrix-shear (1),
       +.BR blind-matrix-translate (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
 (DIR) diff --git a/man/blind.7 b/man/blind.7
       @@ -133,6 +133,27 @@ Generate a video with a linear gradient
        .BR blind-make-kernel (1)
        Create a custom convolution matrix
        .TP
       +.BR blind-matrix-orthoproject (1)
       +Create an affine 2D-transformation matrix for othogonal projection
       +.TP
       +.BR blind-matrix-reflect (1)
       +Create an affine 2D-transformation matrix for reflection about a line
       +.TP
       +.BR blind-matrix-rotate (1)
       +Create an affine 2D-transformation matrix for rotation
       +.TP
       +.BR blind-matrix-scale (1)
       +Create an affine 2D-transformation matrix for scaling
       +.TP
       +.BR blind-matrix-shear (1)
       +Create an affine 2D-transformation matrix for shearing
       +.TP
       +.BR blind-matrix-translate (1)
       +Create an affine 2D-transformation matrix for translation
       +.TP
       +.BR blind-matrix-transpose (1)
       +Create an affine 2D-transformation matrix for transposition
       +.TP
        .BR blind-mosaic (1)
        Redraw each frame in video as a mosaic
        .TP
 (DIR) diff --git a/src/blind-matrix-orthoproject.c b/src/blind-matrix-orthoproject.c
       @@ -0,0 +1,89 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-c]")
       +
       +static int per_channel = 0;
       +
       +#define PROCESS(TYPE)\
       +        do {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t matrix[9];\
       +                pixel_t buf[2];\
       +                TYPE x2, y2, norm2;\
       +                int i;\
       +                \
       +                for (i = 0; i < 4; i++) {\
       +                        matrix[0][i] = 1, matrix[1][i] = 0, matrix[2][i] = 0;\
       +                        matrix[3][i] = 0, matrix[4][i] = 1, matrix[5][i] = 0;\
       +                        matrix[6][i] = 0, matrix[7][i] = 0, matrix[8][i] = 1;\
       +                }\
       +                \
       +                while (eread_frame(stream, buf)) {\
       +                        if (per_channel) {\
       +                                for (i = 0; i < 4; i++) {\
       +                                        x2 = buf[0][i] * buf[0][i];\
       +                                        y2 = buf[1][i] * buf[1][i];\
       +                                        norm2 = x2 + y2;\
       +                                        matrix[0][i] = x2 / norm2;\
       +                                        matrix[4][i] = y2 / norm2;\
       +                                        matrix[3][i] = matrix[1][i] = buf[0][i] * buf[1][i] / norm2;\
       +                                }\
       +                        } else {\
       +                                buf[0][1] *= buf[0][3];\
       +                                buf[1][1] *= buf[1][3];\
       +                                x2 = buf[0][1] * buf[0][1];\
       +                                y2 = buf[1][1] * buf[1][1];\
       +                                norm2 = x2 + y2;\
       +                                matrix[0][0] = x2 / norm2;\
       +                                matrix[4][0] = y2 / norm2;\
       +                                matrix[3][0] = matrix[1][0] = buf[0][1] * buf[1][1] / norm2;\
       +                                matrix[0][3] = matrix[0][2] = matrix[0][1] = matrix[0][0];\
       +                                matrix[1][3] = matrix[1][2] = matrix[1][1] = matrix[1][0];\
       +                                matrix[3][3] = matrix[3][2] = matrix[3][1] = matrix[3][0];\
       +                                matrix[4][3] = matrix[4][2] = matrix[4][1] = matrix[4][0];\
       +                        }\
       +                        ewriteall(STDOUT_FILENO, matrix, sizeof(matrix), "<stdout>");\
       +                }\
       +        } while (0)
       +
       +static void process_lf(struct stream *stream) { PROCESS(double); }
       +static void process_f(struct stream *stream) { PROCESS(float); }
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'c':
       +                per_channel = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (stream.width > 2 || stream.height > 2 || stream.width * stream.height != 2)
       +                eprintf("<stdin>: each frame must contain exactly 2 pixels\n");
       +
       +        stream.width  = 3;
       +        stream.height = 3;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +
       +        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);
       +
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-matrix-reflect.c b/src/blind-matrix-reflect.c
       @@ -0,0 +1,87 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-c]")
       +
       +static int per_channel = 0;
       +
       +#define PROCESS(TYPE)\
       +        do {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t matrix[9];\
       +                pixel_t buf[2];\
       +                TYPE x2, y2, norm2;\
       +                int i;\
       +                \
       +                for (i = 0; i < 4; i++) {\
       +                        matrix[0][i] = 1, matrix[1][i] = 0, matrix[2][i] = 0;\
       +                        matrix[3][i] = 0, matrix[4][i] = 1, matrix[5][i] = 0;\
       +                        matrix[6][i] = 0, matrix[7][i] = 0, matrix[8][i] = 1;\
       +                }\
       +                \
       +                while (eread_frame(stream, buf)) {\
       +                        if (per_channel) {\
       +                                for (i = 0; i < 4; i++) {\
       +                                        x2 = buf[0][i] * buf[0][i];\
       +                                        y2 = buf[1][i] * buf[1][i];\
       +                                        norm2 = x2 + y2;\
       +                                        matrix[4][i] = -(matrix[0][i] = (x2 - y2) / norm2);\
       +                                        matrix[3][i] = matrix[1][i] = 2 * buf[0][i] * buf[1][i] / norm2;\
       +                                }\
       +                        } else {\
       +                                buf[0][1] *= buf[0][3];\
       +                                buf[1][1] *= buf[1][3];\
       +                                x2 = buf[0][1] * buf[0][1];\
       +                                y2 = buf[1][1] * buf[1][1];\
       +                                norm2 = x2 + y2;\
       +                                matrix[4][0] = -(matrix[0][0] = (x2 - y2) / norm2);\
       +                                matrix[3][0] = matrix[1][0] = 2 * buf[0][1] * buf[1][1] / norm2;\
       +                                matrix[0][3] = matrix[0][2] = matrix[0][1] = matrix[0][0];\
       +                                matrix[1][3] = matrix[1][2] = matrix[1][1] = matrix[1][0];\
       +                                matrix[3][3] = matrix[3][2] = matrix[3][1] = matrix[3][0];\
       +                                matrix[4][3] = matrix[4][2] = matrix[4][1] = matrix[4][0];\
       +                        }\
       +                        ewriteall(STDOUT_FILENO, matrix, sizeof(matrix), "<stdout>");\
       +                }\
       +        } while (0)
       +
       +static void process_lf(struct stream *stream) { PROCESS(double); }
       +static void process_f(struct stream *stream) { PROCESS(float); }
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'c':
       +                per_channel = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (stream.width > 2 || stream.height > 2 || stream.width * stream.height != 2)
       +                eprintf("<stdin>: each frame must contain exactly 2 pixels\n");
       +
       +        stream.width  = 3;
       +        stream.height = 3;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +
       +        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);
       +
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-matrix-rotate.c b/src/blind-matrix-rotate.c
       @@ -0,0 +1,79 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-c]")
       +
       +static int per_channel = 0;
       +
       +#define PROCESS(TYPE)\
       +        do {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t matrix[9];\
       +                pixel_t buf;\
       +                int i;\
       +                \
       +                for (i = 0; i < 4; i++) {\
       +                        matrix[0][i] = 1, matrix[1][i] = 0, matrix[2][i] = 0;\
       +                        matrix[3][i] = 0, matrix[4][i] = 1, matrix[5][i] = 0;\
       +                        matrix[6][i] = 0, matrix[7][i] = 0, matrix[8][i] = 1;\
       +                }\
       +                \
       +                while (eread_frame(stream, buf)) {\
       +                        if (per_channel) {\
       +                                for (i = 0; i < 4; i++) {\
       +                                        matrix[4][i] = matrix[0][i] = cos(buf[i]);\
       +                                        matrix[3][i] = -(matrix[1][i] = sin(buf[i]));\
       +                                }\
       +                        } else {\
       +                                buf[1] *= buf[3];\
       +                                matrix[4][0] = matrix[0][0] = cos(buf[1]);\
       +                                matrix[3][0] = -(matrix[1][0] = sin(buf[1]));\
       +                                matrix[0][3] = matrix[0][2] = matrix[0][1] = matrix[0][0];\
       +                                matrix[1][3] = matrix[0][2] = matrix[0][1] = matrix[1][0];\
       +                                matrix[3][3] = matrix[0][2] = matrix[0][1] = matrix[3][0];\
       +                                matrix[4][3] = matrix[0][2] = matrix[0][1] = matrix[4][0];\
       +                        }\
       +                        ewriteall(STDOUT_FILENO, matrix, sizeof(matrix), "<stdout>");\
       +                }\
       +        } while (0)
       +
       +static void process_lf(struct stream *stream) { PROCESS(double); }
       +static void process_f(struct stream *stream) { PROCESS(float); }
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'c':
       +                per_channel = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (stream.width != 1 && stream.height != 1)
       +                eprintf("<stdin>: each frame must contain exactly 1 pixels\n");
       +
       +        stream.width  = 3;
       +        stream.height = 3;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +
       +        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);
       +
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-matrix-scale.c b/src/blind-matrix-scale.c
       @@ -0,0 +1,74 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-c]")
       +
       +static int per_channel = 0;
       +
       +#define PROCESS(TYPE)\
       +        do {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t matrix[9];\
       +                pixel_t buf[2];\
       +                int i;\
       +                \
       +                for (i = 0; i < 4; i++) {\
       +                        matrix[0][i] = 1, matrix[1][i] = 0, matrix[2][i] = 0;\
       +                        matrix[3][i] = 0, matrix[4][i] = 1, matrix[5][i] = 0;\
       +                        matrix[6][i] = 0, matrix[7][i] = 0, matrix[8][i] = 1;\
       +                }\
       +                \
       +                while (eread_frame(stream, buf)) {\
       +                        if (per_channel) {\
       +                                for (i = 0; i < 4; i++) {\
       +                                        matrix[0][i] = buf[0][i];\
       +                                        matrix[4][i] = buf[1][i];\
       +                                }\
       +                        } else {\
       +                                matrix[0][3] = matrix[0][2] = matrix[0][1] = matrix[0][0] = buf[0][1] * buf[0][3];\
       +                                matrix[4][3] = matrix[4][2] = matrix[4][1] = matrix[4][0] = buf[1][1] * buf[1][3];\
       +                        }\
       +                        ewriteall(STDOUT_FILENO, matrix, sizeof(matrix), "<stdout>");\
       +                }\
       +        } while (0)
       +
       +static void process_lf(struct stream *stream) { PROCESS(double); }
       +static void process_f(struct stream *stream) { PROCESS(float); }
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'c':
       +                per_channel = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (stream.width > 2 || stream.height > 2 || stream.width * stream.height != 2)
       +                eprintf("<stdin>: each frame must contain exactly 2 pixels\n");
       +
       +        stream.width  = 3;
       +        stream.height = 3;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +
       +        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);
       +
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-matrix-shear.c b/src/blind-matrix-shear.c
       @@ -0,0 +1,84 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-ac]")
       +
       +static int by_angle = 0;
       +static int per_channel = 0;
       +
       +#define PROCESS(TYPE)\
       +        do {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t matrix[9];\
       +                pixel_t buf[2];\
       +                int i;\
       +                \
       +                for (i = 0; i < 4; i++) {\
       +                        matrix[0][i] = 1, matrix[1][i] = 0, matrix[2][i] = 0;\
       +                        matrix[3][i] = 0, matrix[4][i] = 1, matrix[5][i] = 0;\
       +                        matrix[6][i] = 0, matrix[7][i] = 0, matrix[8][i] = 1;\
       +                }\
       +                \
       +                while (eread_frame(stream, buf)) {\
       +                        if (by_angle) {\
       +                                for (i = !per_channel; i < (per_channel ? 4 : 2); i++) {\
       +                                        buf[0][i] = tan(buf[0][i]);\
       +                                        buf[1][i] = tan(buf[1][i]);\
       +                                }\
       +                        }\
       +                        if (per_channel) {\
       +                                for (i = 0; i < 4; i++) {\
       +                                        matrix[1][i] = buf[0][i];\
       +                                        matrix[3][i] = buf[1][i];\
       +                                }\
       +                        } else {\
       +                                matrix[1][3] = matrix[1][2] = matrix[1][1] = matrix[1][0] = buf[0][1] * buf[0][3];\
       +                                matrix[3][3] = matrix[3][2] = matrix[3][1] = matrix[3][0] = buf[1][1] * buf[1][3];\
       +                        }\
       +                        ewriteall(STDOUT_FILENO, matrix, sizeof(matrix), "<stdout>");\
       +                }\
       +        } while (0)
       +
       +static void process_lf(struct stream *stream) { PROCESS(double); }
       +static void process_f(struct stream *stream) { PROCESS(float); }
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'a':
       +                by_angle = 1;
       +                break;
       +        case 'c':
       +                per_channel = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (stream.width > 2 || stream.height > 2 || stream.width * stream.height != 2)
       +                eprintf("<stdin>: each frame must contain exactly 2 pixels\n");
       +
       +        stream.width  = 3;
       +        stream.height = 3;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +
       +        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);
       +
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-matrix-translate.c b/src/blind-matrix-translate.c
       @@ -0,0 +1,74 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-c]")
       +
       +static int per_channel = 0;
       +
       +#define PROCESS(TYPE)\
       +        do {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t matrix[9];\
       +                pixel_t buf[2];\
       +                int i;\
       +                \
       +                for (i = 0; i < 4; i++) {\
       +                        matrix[0][i] = 1, matrix[1][i] = 0, matrix[2][i] = 0;\
       +                        matrix[3][i] = 0, matrix[4][i] = 1, matrix[5][i] = 0;\
       +                        matrix[6][i] = 0, matrix[7][i] = 0, matrix[8][i] = 1;\
       +                }\
       +                \
       +                while (eread_frame(stream, buf)) {\
       +                        if (per_channel) {\
       +                                for (i = 0; i < 4; i++) {\
       +                                        matrix[2][i] = buf[0][i];\
       +                                        matrix[5][i] = buf[1][i];\
       +                                }\
       +                        } else {\
       +                                matrix[2][3] = matrix[2][2] = matrix[2][1] = matrix[2][0] = buf[0][1] * buf[0][3];\
       +                                matrix[5][3] = matrix[5][2] = matrix[5][1] = matrix[5][0] = buf[1][1] * buf[1][3];\
       +                        }\
       +                        ewriteall(STDOUT_FILENO, matrix, sizeof(matrix), "<stdout>");\
       +                }\
       +        } while (0)
       +
       +static void process_lf(struct stream *stream) { PROCESS(double); }
       +static void process_f(struct stream *stream) { PROCESS(float); }
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'c':
       +                per_channel = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (stream.width > 2 || stream.height > 2 || stream.width * stream.height != 2)
       +                eprintf("<stdin>: each frame must contain exactly 2 pixels\n");
       +
       +        stream.width  = 3;
       +        stream.height = 3;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +
       +        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);
       +
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/blind-matrix-transpose.c b/src/blind-matrix-transpose.c
       @@ -0,0 +1,76 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-c]")
       +
       +static int per_channel = 0;
       +
       +#define PROCESS(TYPE)\
       +        do {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t matrix[9];\
       +                pixel_t buf;\
       +                int i;\
       +                \
       +                for (i = 0; i < 4; i++) {\
       +                        matrix[0][i] = 1, matrix[1][i] = 0, matrix[2][i] = 0;\
       +                        matrix[3][i] = 0, matrix[4][i] = 1, matrix[5][i] = 0;\
       +                        matrix[6][i] = 0, matrix[7][i] = 0, matrix[8][i] = 1;\
       +                }\
       +                \
       +                while (eread_frame(stream, buf)) {\
       +                        if (per_channel) {\
       +                                for (i = 0; i < 4; i++) {\
       +                                        matrix[3][i] = matrix[1][i] = buf[i];\
       +                                        matrix[4][i] = matrix[0][i] = 1 - buf[i];\
       +                                }\
       +                        } else {\
       +                                for (i = 0; i < 4; i++) {\
       +                                        matrix[3][i] = matrix[1][i] = buf[1] * buf[3];\
       +                                        matrix[4][i] = matrix[0][i] = 1 - matrix[3][i];\
       +                                }\
       +                        }\
       +                        ewriteall(STDOUT_FILENO, matrix, sizeof(matrix), "<stdout>");\
       +                }\
       +        } while (0)
       +
       +static void process_lf(struct stream *stream) { PROCESS(double); }
       +static void process_f(struct stream *stream) { PROCESS(float); }
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        void (*process)(struct stream *stream);
       +
       +        ARGBEGIN {
       +        case 'c':
       +                per_channel = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc)
       +                usage();
       +
       +        eopen_stream(&stream, NULL);
       +
       +        if (stream.width != 1 && stream.height != 12)
       +                eprintf("<stdin>: each frame must contain exactly 1 pixels\n");
       +
       +        stream.width  = 3;
       +        stream.height = 3;
       +        fprint_stream_head(stdout, &stream);
       +        efflush(stdout, "<stdout>");
       +
       +        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);
       +
       +        process(&stream);
       +        return 0;
       +}
 (DIR) diff --git a/src/video-math.h b/src/video-math.h
       @@ -71,6 +71,7 @@ posmodf(float a, float b)
        #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 tan(...)         MATH_GENERIC_1(tan,      __VA_ARGS__)
        #define atan2(...)       MATH_GENERIC_N(atan2,    __VA_ARGS__)
        
        #define srgb_encode(...) BLIND_GENERIC_1(srgb_encode, __VA_ARGS__)