Add blind-multiply-matrices - 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 1d6f31b2f6092e05056dc6a3d97b03a251f110e1
 (DIR) parent edc0a1ca9c1c85a77ac04eb78c8fa208e0c5b24f
 (HTM) Author: Mattias Andrée <maandree@kth.se>
       Date:   Fri, 14 Jul 2017 17:38:11 +0200
       
       Add blind-multiply-matrices
       
       Signed-off-by: Mattias Andrée <maandree@kth.se>
       
       Diffstat:
         M Makefile                            |       1 +
         M README                              |       3 +++
         M man/blind-matrix-orthoproject.1     |       3 ++-
         M man/blind-matrix-reflect.1          |       3 ++-
         M man/blind-matrix-rotate.1           |       3 ++-
         M man/blind-matrix-scale.1            |       3 ++-
         M man/blind-matrix-shear.1            |       3 ++-
         M man/blind-matrix-translate.1        |       3 ++-
         M man/blind-matrix-transpose.1        |       3 ++-
         A man/blind-multiply-matrices.1       |      43 ++++++++++++++++++++++++++++++
         M man/blind-stack.1                   |       2 +-
         M man/blind.7                         |       3 +++
         A src/blind-multiply-matrices.c       |     138 ++++++++++++++++++++++++++++++
       
       13 files changed, 203 insertions(+), 8 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -50,6 +50,7 @@ BIN =\
                blind-matrix-transpose\
                blind-mosaic\
                blind-mosaic-edges\
       +        blind-multiply-matrices\
                blind-next-frame\
                blind-norm\
                blind-quaternion-product\
 (DIR) diff --git a/README b/README
       @@ -144,6 +144,9 @@ UTILITIES
               blind-mosaic-edges(1)
                      Find edges in a mosaic video
        
       +       blind-multiply-matrices(1)
       +              Multiply matrix-vidoes
       +
               blind-next-frame(1)
                      Extracts the next frame from a video
        
 (DIR) diff --git a/man/blind-matrix-orthoproject.1 b/man/blind-matrix-orthoproject.1
       @@ -37,7 +37,8 @@ channels in stdout.
        .BR blind-matrix-scale (1),
        .BR blind-matrix-shear (1),
        .BR blind-matrix-translate (1),
       -.BR blind-matrix-transpose (1)
       +.BR blind-matrix-transpose (1),
       +.BR blind-multiply-matrices (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
       @@ -37,7 +37,8 @@ channels in stdout.
        .BR blind-matrix-scale (1),
        .BR blind-matrix-shear (1),
        .BR blind-matrix-translate (1),
       -.BR blind-matrix-transpose (1)
       +.BR blind-matrix-transpose (1),
       +.BR blind-multiply-matrices (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
       @@ -39,7 +39,8 @@ grows upwards rather than downwards.
        .BR blind-matrix-scale (1),
        .BR blind-matrix-shear (1),
        .BR blind-matrix-translate (1),
       -.BR blind-matrix-transpose (1)
       +.BR blind-matrix-transpose (1),
       +.BR blind-multiply-matrices (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
       @@ -37,7 +37,8 @@ channels in stdout.
        .BR blind-matrix-rotate (1),
        .BR blind-matrix-shear (1),
        .BR blind-matrix-translate (1),
       -.BR blind-matrix-transpose (1)
       +.BR blind-matrix-transpose (1),
       +.BR blind-multiply-matrices (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
       @@ -52,7 +52,8 @@ other.
        .BR blind-matrix-rotate (1),
        .BR blind-matrix-scale (1),
        .BR blind-matrix-translate (1),
       -.BR blind-matrix-transpose (1)
       +.BR blind-matrix-transpose (1),
       +.BR blind-multiply-matrices (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
       @@ -41,7 +41,8 @@ Y-axis grows upwards rather than downwards.
        .BR blind-matrix-rotate (1),
        .BR blind-matrix-scale (1),
        .BR blind-matrix-shear (1),
       -.BR blind-matrix-transpose (1)
       +.BR blind-matrix-transpose (1),
       +.BR blind-multiply-matrices (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
       @@ -37,7 +37,8 @@ channels in stdout.
        .BR blind-matrix-rotate (1),
        .BR blind-matrix-scale (1),
        .BR blind-matrix-shear (1),
       -.BR blind-matrix-translate (1)
       +.BR blind-matrix-translate (1),
       +.BR blind-multiply-matrices (1)
        .SH AUTHORS
        Mattias Andrée
        .RI < maandree@kth.se >
 (DIR) diff --git a/man/blind-multiply-matrices.1 b/man/blind-multiply-matrices.1
       @@ -0,0 +1,43 @@
       +.TH BLIND-MULTIPLY-MATRICES 1 blind
       +.SH NAME
       +blind-multiply-matrices - Multiply matrix-vidoes
       +.SH SYNOPSIS
       +.B blind-multiply-matrices
       +[-en]
       +.I leftmost-stream
       +.RI "... " rightmost-stream
       +.SH DESCRIPTION
       +.B blind-multiply-matrices
       +reads videos representing matrices from the files
       +specified via the arguments
       +.I leftmost-stream
       +to
       +.IR rightmost-stream ,
       +and multiples them, frame by frame, from right to
       +left, and prints the resulting video to stdout.
       +Each frame in a video contains one matrix per
       +channel, made up from the values on a channel
       +of each pixel.
       +.SH OPTIONS
       +.TP
       +.B -e
       +Apply optimisation that assumes all channels
       +are identical.
       +.TP
       +.B -n
       +Reverse the order of each specified files,
       +so that the transformations are applied in
       +the order they are specified rather than
       +in reverse order.
       +.SH SEE ALSO
       +.BR blind (7),
       +.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),
       +.BR blind-matrix-transpose (1)
       +.SH AUTHORS
       +Mattias Andrée
       +.RI < maandree@kth.se >
 (DIR) diff --git a/man/blind-stack.1 b/man/blind-stack.1
       @@ -4,7 +4,7 @@ blind-stack - Overlay videos
        .SH SYNOPSIS
        .B blind-stack
        [-bs]
       -.IR bottom-stream
       +.I bottom-stream
        .RI "... " top-stream
        .SH DESCRIPTION
        .B blind-stack
 (DIR) diff --git a/man/blind.7 b/man/blind.7
       @@ -160,6 +160,9 @@ Redraw each frame in video as a mosaic
        .BR blind-mosaic-edges (1)
        Find edges in a mosaic video
        .TP
       +.BR blind-multiply-matrices (1)
       +Multiply matrix-vidoes
       +.TP
        .BR blind-next-frame (1)
        Extracts the next frame from a video
        .TP
 (DIR) diff --git a/src/blind-multiply-matrices.c b/src/blind-multiply-matrices.c
       @@ -0,0 +1,138 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("[-en] leftmost-stream ... rightmost-stream")
       +
       +static int equal = 0;
       +static size_t max_frame_size;
       +
       +#define PROCESS(TYPE)\
       +        do {\
       +                typedef TYPE pixel_t[4];\
       +                pixel_t *res, *left, *right, *tmp;\
       +                size_t i, j, w, h, h2, x, y, k, r;\
       +                res = emalloc(max_frame_size);\
       +                left = emalloc(max_frame_size);\
       +                right = emalloc(max_frame_size);\
       +                \
       +                while (eread_frame(streams + (n_streams - 1), res)) {\
       +                        w = streams[n_streams - 1].width;\
       +                        h = streams[n_streams - 1].height;\
       +                        for (i = n_streams - 1; i--;) {\
       +                                tmp = res, res = right, right = tmp;\
       +                                if (!eread_frame(streams + i, left))\
       +                                        goto done;\
       +                                h2 = streams[i].height;\
       +                                memset(res, 0, w * h2 * streams->pixel_size);\
       +                                \
       +                                /* XXX Is there any significant performance to be gained by transposing `right`? */\
       +                                if (equal) {\
       +                                        for (y = r = 0; y < h2; y++) {\
       +                                                for (x = 0; x < w; x++, r++) {\
       +                                                        for (k = 0; k < h; k++) \
       +                                                                res[r][1] += left[y * h + k][1] * right[k * w + x][1];\
       +                                                        res[r][3] = res[r][2] = res[r][0] = res[r][1];\
       +                                                }\
       +                                        }\
       +                                } else {\
       +                                        for (y = r = 0; y < h2; y++)\
       +                                                for (x = 0; x < w; x++, r++) \
       +                                                        for (k = 0; k < h; k++)\
       +                                                                for (j = 0; j < 4; j++)\
       +                                                                        res[r][j] += left[y * h + k][j] * right[k * w + x][j];\
       +                                }\
       +                                \
       +                                h = h2;\
       +                        }\
       +                        ewriteall(STDOUT_FILENO, res, streams->frame_size, "<stdout>");\
       +                }\
       +                \
       +        done:\
       +                free(res);\
       +                free(left);\
       +                free(right);\
       +        } while (0)
       +
       +static void process_lf(struct stream *streams, size_t n_streams) { PROCESS(double); }
       +static void process_f (struct stream *streams, size_t n_streams) { PROCESS(float); }
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream *streams;
       +        size_t n_streams, i, frames = 0;
       +        int natural = 0, j;
       +        char **rev_argv;
       +        size_t max_width = 0, max_height = 0;
       +        size_t width = 0, height = 0, w, h;
       +        void (*process)(struct stream *streams, size_t n_streams);
       +
       +        ARGBEGIN {
       +        case 'e':
       +                equal = 1;
       +                break;
       +        case 'n':
       +                natural = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND;
       +
       +        if (argc < 2)
       +                usage();
       +
       +        if (natural) {
       +                rev_argv = alloca(argc * sizeof(*rev_argv));
       +                for (j = 0; j < argc; j++)
       +                        rev_argv[j] = argv[argc - 1 - j];
       +                argv = rev_argv;
       +        }
       +
       +        n_streams = (size_t)argc;
       +        streams = ecalloc(n_streams, sizeof(*streams));
       +
       +        for (i = 0; i < n_streams; i++) {
       +                eopen_stream(streams + i, argv[i]);
       +                if (streams[i].frames && streams[i].frames < frames)
       +                        frames = streams[i].frames;
       +                if (streams->width > max_width)
       +                        max_width = streams->width;
       +                if (streams->height > max_height)
       +                        max_height = streams->height;
       +        }
       +        for (i = 1; i < n_streams; i++)
       +                if (strcmp(streams->pixfmt, streams[i].pixfmt))
       +                        eprintf("videos use incompatible pixel formats\n");
       +
       +        width  = streams[n_streams - 1].width;
       +        height = streams[n_streams - 1].height;
       +        for (i = n_streams - 1; i--;) {
       +                if (streams[i].width != height)
       +                        eprintf("videos do not have the compatible geometry\n");
       +                height = streams[i].height;
       +        }
       +
       +        if (!strcmp(streams->pixfmt, "xyza"))
       +                process = process_lf;
       +        else if (!strcmp(streams->pixfmt, "xyza f"))
       +                process = process_f;
       +        else
       +                eprintf("pixel format %s is not supported, try xyza\n", streams->pixfmt);
       +
       +        w = streams->width,  streams->width  = max_width;
       +        h = streams->height, streams->height = max_height;
       +        echeck_dimensions(streams, WIDTH | HEIGHT, NULL);
       +        streams->width  = width;
       +        streams->height = height;
       +        streams->frames = frames;
       +        fprint_stream_head(stdout, streams);
       +        streams->width  = w;
       +        streams->height = h;
       +        efflush(stdout, "<stdout>");
       +        max_frame_size = max_width * max_height * streams->pixel_size;
       +
       +        process(streams, n_streams);
       +
       +        free(streams);
       +        return 0;
       +}