URI:
       Add blind-disperse, blind-split-rows, and blind-split-cols - blind - suckless command-line video editing utility
  HTML git clone git://git.suckless.org/blind
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit b518ce8977c0b97dd2236fc8cbc7dad1dd70511b
   DIR parent afb5da1566586f6f8f53a8b7e443b8bca70542fe
  HTML Author: Mattias Andrée <maandree@kth.se>
       Date:   Fri,  2 Jun 2017 21:00:47 +0200
       
       Add blind-disperse, blind-split-rows, and blind-split-cols
       
       Signed-off-by: Mattias Andrée <maandree@kth.se>
       
       Diffstat:
         M Makefile                            |       3 +++
         M TODO                                |      10 ++++++----
         A src/blind-disperse.c                |      48 +++++++++++++++++++++++++++++++
         A src/blind-split-cols.c              |      47 +++++++++++++++++++++++++++++++
         A src/blind-split-rows.c              |      47 +++++++++++++++++++++++++++++++
         M src/stream.c                        |      51 +++++++++++++++++++++++++++++--
         M src/stream.h                        |      34 +++++++++++++++++++++++--------
       
       7 files changed, 226 insertions(+), 14 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -11,6 +11,7 @@ BIN =\
                blind-crop\
                blind-cut\
                blind-decompress\
       +        blind-disperse\
                blind-dissolve\
                blind-extend\
                blind-flip\
       @@ -34,6 +35,8 @@ BIN =\
                blind-single-colour\
                blind-skip-pattern\
                blind-split\
       +        blind-split-cols\
       +        blind-split-rows\
                blind-stack\
                blind-tee\
                blind-time-blur\
   DIR diff --git a/TODO b/TODO
       @@ -1,3 +1,9 @@
       +Write manpages for:
       +        blind-disperse                inverse of blind-interleave
       +                                        Useful for processing a video on multiple computers
       +        blind-split-rows        split stream into multiple streams by splitting video horizontally
       +        blind-split-cols        split stream into multiple streams by splitting video vertically
       +
        blind-transform                affine transformation by matrix multiplication, -t for tiling, -s for
                                        improve quality on downscaling (pixels' neighbours must not change).
        blind-chroma-key        replace a chroma with transparency.
       @@ -17,8 +23,6 @@ blind-affine-colour        apply an affine transformation to the colour of each pixel,
        blind-invert-chroma        invert the chroma
        blind-from-sent                convert a sent presentation to a one-frame-per-slide blind video.
        blind-interleave        framewise interleave videos
       -blind-disperse                inverse of blind-interleave
       -                                Useful for processing a video on multiple computers
        
        blind-kirsch                https://en.wikipedia.org/wiki/Kirsch_operator
        blind-gaussian-noise        https://en.wikipedia.org/wiki/Gaussian_noise
       @@ -44,8 +48,6 @@ blind-mean                mean of multiple streams
                                https://en.wikipedia.org/wiki/Logarithmic_mean
                                https://en.wikipedia.org/wiki/Stolarsky_mean
        blind-temporal-arithm        blind-arithm but over all frames in a video instead of over all streams
       -blind-split-rows        split stream into multiple streams by splitting video horizontally
       -blind-split-cols        split stream into multiple streams by splitting video vertically
        blind-cat-rows                merge video by vertically stacking streams (inverse of blind-split-rows)
        blind-cat-cols                merge video by putting streams beside each other (inverse of blind-split-cols)
        
   DIR diff --git a/src/blind-disperse.c b/src/blind-disperse.c
       @@ -0,0 +1,48 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("(file frames) ...")
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        size_t *frames, *framecount, period = 0, parts, i, n;
       +        int *fds;
       +
       +        UNOFLAGS(argc % 2 || !argc);
       +
       +        eopen_stream(&stream, NULL);
       +
       +        parts      = (size_t)argc / 2;
       +        frames     = alloca(parts * sizeof(*frames));
       +        framecount = alloca(parts * sizeof(*framecount));
       +        fds        = alloca(parts * sizeof(*fds));
       +
       +        for (i = 0; i < parts; i++) {
       +                fds[i] = eopen(argv[i * 2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
       +                frames[i] = etozu_arg("frames", argv[i * 2 + 1], 1, SIZE_MAX);
       +        }
       +        for (i = 0; i < parts; i++) {
       +                if (frames[i] > SIZE_MAX - period)
       +                        eprintf("the sum of selected frame intervals exceeds %zu\n", SIZE_MAX);
       +                period += frames[i];
       +        }
       +        for (n = stream.frames / period, i = 0; i < parts; i++)
       +                framecount[i] = n * frames[i];
       +        for (n = stream.frames % period, i = 0; i < parts; i++) {
       +                framecount[i] += MIN(n, frames[i]);
       +                n -= MIN(n, frames[i]);
       +        }
       +
       +        for (i = 0; i < parts; i++)
       +                if (DPRINTF_HEAD(fds[i], framecount[i], stream.width, stream.height, stream.pixfmt) < 0)
       +                        eprintf("dprintf %s:", argv[i * 2]);
       +        for (i = 0; i < parts; i++, i = i == parts ? 0 : i)
       +                if (esend_frames(&stream, fds[i], frames[i], argv[i * 2]) != frames[i])
       +                        break;
       +        for (i = 0; i < parts; i++)
       +                close(fds[i]);
       +
       +        return 0;
       +}
   DIR diff --git a/src/blind-split-cols.c b/src/blind-split-cols.c
       @@ -0,0 +1,47 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("(file columns) ...")
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        size_t *cols, period = 0, parts, i;
       +        int *fds;
       +
       +        UNOFLAGS(argc % 2 || !argc);
       +
       +        eopen_stream(&stream, NULL);
       +
       +        parts = (size_t)argc / 2;
       +        cols  = alloca(parts * sizeof(*cols));
       +        fds   = alloca(parts * sizeof(*fds));
       +
       +        for (i = 0; i < parts; i++) {
       +                fds[i] = eopen(argv[i * 2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
       +                cols[i] = etozu_arg("columns", argv[i * 2 + 1], 1, SIZE_MAX);
       +        }
       +        for (i = 0; i < parts; i++) {
       +                if (cols[i] > SIZE_MAX - period)
       +                        goto bad_col_count;
       +                period += cols[i];
       +        }
       +        if (period != stream.width)
       +                goto bad_col_count;
       +
       +        for (i = 0; i < parts; i++)
       +                if (DPRINTF_HEAD(fds[i], stream.frames, cols[i], stream.height, stream.pixfmt) < 0)
       +                        eprintf("dprintf %s:", argv[i * 2]);
       +        for (i = 0; i < parts; i++, i = i == parts ? 0 : i)
       +                if (esend_pixels(&stream, fds[i], cols[i], argv[i * 2]) != cols[i])
       +                        break;
       +        for (i = 0; i < parts; i++)
       +                close(fds[i]);
       +
       +        return 0;
       +
       +bad_col_count:
       +        eprintf("the sum of all columns must add up to the width of the input video\n");
       +        return 1;
       +}
   DIR diff --git a/src/blind-split-rows.c b/src/blind-split-rows.c
       @@ -0,0 +1,47 @@
       +/* See LICENSE file for copyright and license details. */
       +#include "common.h"
       +
       +USAGE("(file rows) ...")
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct stream stream;
       +        size_t *rows, period = 0, parts, i;
       +        int *fds;
       +
       +        UNOFLAGS(argc % 2 || !argc);
       +
       +        eopen_stream(&stream, NULL);
       +
       +        parts = (size_t)argc / 2;
       +        rows  = alloca(parts * sizeof(*rows));
       +        fds   = alloca(parts * sizeof(*fds));
       +
       +        for (i = 0; i < parts; i++) {
       +                fds[i] = eopen(argv[i * 2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
       +                rows[i] = etozu_arg("rows", argv[i * 2 + 1], 1, SIZE_MAX);
       +        }
       +        for (i = 0; i < parts; i++) {
       +                if (rows[i] > SIZE_MAX - period)
       +                        goto bad_row_count;
       +                period += rows[i];
       +        }
       +        if (period != stream.height)
       +                goto bad_row_count;
       +
       +        for (i = 0; i < parts; i++)
       +                if (DPRINTF_HEAD(fds[i], stream.frames, stream.width, rows[i], stream.pixfmt) < 0)
       +                        eprintf("dprintf %s:", argv[i * 2]);
       +        for (i = 0; i < parts; i++, i = i == parts ? 0 : i)
       +                if (esend_rows(&stream, fds[i], rows[i], argv[i * 2]) != rows[i])
       +                        break;
       +        for (i = 0; i < parts; i++)
       +                close(fds[i]);
       +
       +        return 0;
       +
       +bad_row_count:
       +        eprintf("the sum of all rows must add up to the height of the input video\n");
       +        return 1;
       +}
   DIR diff --git a/src/stream.c b/src/stream.c
       @@ -270,8 +270,7 @@ enread_segment(int status, struct stream *stream, void *buf, size_t n)
        size_t
        ensend_frames(int status, struct stream *stream, int outfd, size_t frames, const char *outfname)
        {
       -        size_t h, w, p, n;
       -        size_t ret = 0;
       +        size_t h, w, p, n, ret;
        
                for (ret = 0; ret < frames; ret++) {
                        for (p = stream->pixel_size; p; p--) {
       @@ -295,6 +294,54 @@ done:
        }
        
        
       +size_t
       +ensend_rows(int status, struct stream *stream, int outfd, size_t rows, const char *outfname)
       +{
       +        size_t w, p, n, ret;
       +
       +        for (ret = 0; ret < rows; ret++) {
       +                for (p = stream->pixel_size; p; p--) {
       +                        for (w = stream->width; w; w -= n, stream->ptr -= n) {
       +                                if (!stream->ptr && !enread_stream(status, stream, w))
       +                                        goto done;
       +                                n = MIN(stream->ptr, w);
       +                                if (outfd >= 0)
       +                                        enwriteall(status, outfd, stream->buf, n, outfname);
       +                        }
       +                }
       +        }
       +
       +        return ret;
       +done:
       +        if (p != stream->pixel_size || w != stream->width)
       +                enprintf(status, "%s: incomplete row", stream->file);
       +        return ret;
       +}
       +
       +
       +size_t
       +ensend_pixels(int status, struct stream *stream, int outfd, size_t pixels, const char *outfname)
       +{
       +        size_t p, n, ret;
       +
       +        for (ret = 0; ret < pixels; ret++) {
       +                for (p = stream->pixel_size; p; p -= n, stream->ptr -= n) {
       +                        if (!stream->ptr && !enread_stream(status, stream, p))
       +                                goto done;
       +                        n = MIN(stream->ptr, p);
       +                        if (outfd >= 0)
       +                                enwriteall(status, outfd, stream->buf, n, outfname);
       +                }
       +        }
       +
       +        return ret;
       +done:
       +        if (p != stream->pixel_size)
       +                enprintf(status, "%s: incomplete pixel", stream->file);
       +        return ret;
       +}
       +
       +
        int
        ensend_stream(int status, struct stream *stream, int outfd, const char *outfname)
        {
   DIR diff --git a/src/stream.h b/src/stream.h
       @@ -5,21 +5,35 @@
        
        #define STREAM_HEAD_MAX (3 * INTSTRLEN(size_t) + sizeof(((struct stream *)0)->pixfmt) + 10)
        
       +#define XPRINTF_HEAD_FMT "%zu %zu %zu %s\n%cuivf"
       +#define XPRINTF_HEAD_ARGS(FRAMES, WIDTH, HEIGHT, PIXFMT)\
       +        (size_t)(FRAMES), (size_t)(WIDTH), (size_t)(HEIGHT), (PIXFMT), 0
       +
       +#define XPRINTF_HEAD_FMT_FMT(FFRAMES, FWIDTH, FHEIGHT)\
       +        FFRAMES" "FWIDTH" "FHEIGHT" %s\n%cuivf"
       +#define XPRINTF_HEAD_FMT_ARGS(FRAMES, WIDTH, HEIGHT, PIXFMT)\
       +        (FRAMES), (WIDTH), (HEIGHT), (PIXFMT), 0
       +
        #define SPRINTF_HEAD_ZN(BUF, FRAMES, WIDTH, HEIGHT, PIXFMT, LENP)\
       -        sprintf(BUF, "%zu %zu %zu %s\n%cuivf%zn",\
       -                (size_t)(FRAMES), (size_t)(WIDTH), (size_t)(HEIGHT), PIXFMT, 0, LENP)
       +        sprintf(BUF, XPRINTF_HEAD_FMT"%zn", XPRINTF_HEAD_ARGS(FRAMES, WIDTH, HEIGHT, PIXFMT), LENP)
        
        #define SPRINTF_HEAD(BUF, FRAMES, WIDTH, HEIGHT, PIXFMT)\
       -        sprintf(BUF, "%zu %zu %zu %s\n%cuivf",\
       -                (size_t)(FRAMES), (size_t)(WIDTH), (size_t)(HEIGHT), PIXFMT, 0)
       +        sprintf(BUF, XPRINTF_HEAD_FMT, XPRINTF_HEAD_ARGS(FRAMES, WIDTH, HEIGHT, PIXFMT))
        
        #define FPRINTF_HEAD(FP, FRAMES, WIDTH, HEIGHT, PIXFMT)\
       -        fprintf(FP, "%zu %zu %zu %s\n%cuivf",\
       -                (size_t)(FRAMES), (size_t)(WIDTH), (size_t)(HEIGHT), PIXFMT, 0)
       +        fprintf(FP, XPRINTF_HEAD_FMT, XPRINTF_HEAD_ARGS(FRAMES, WIDTH, HEIGHT, PIXFMT))
       +
       +#define DPRINTF_HEAD(FD, FRAMES, WIDTH, HEIGHT, PIXFMT)\
       +        dprintf(FD, XPRINTF_HEAD_FMT, XPRINTF_HEAD_ARGS(FRAMES, WIDTH, HEIGHT, PIXFMT))
       +
       +#define SPRINTF_HEAD_FMT(BUF, FFRAMES, FRAMES, FWIDTH, WIDTH, FHEIGHT, HEIGHT, PIXFMT)\
       +        sprintf(BUF, XPRINTF_HEAD_FMT_FMT(FFRAMES, FWIDTH, FHEIGHT), XPRINTF_HEAD_FMT_ARGS(FRAMES, WIDTH, HEIGHT, PIXFMT))
        
        #define FPRINTF_HEAD_FMT(FP, FFRAMES, FRAMES, FWIDTH, WIDTH, FHEIGHT, HEIGHT, PIXFMT)\
       -        fprintf(FP, FFRAMES" "FWIDTH" "FHEIGHT" %s\n%cuivf",\
       -                FRAMES, WIDTH, HEIGHT, PIXFMT, 0)
       +        fprintf(FP, XPRINTF_HEAD_FMT_FMT(FFRAMES, FWIDTH, FHEIGHT), XPRINTF_HEAD_FMT_ARGS(FRAMES, WIDTH, HEIGHT, PIXFMT))
       +
       +#define DPRINTF_HEAD_FMT(FD, FFRAMES, FRAMES, FWIDTH, WIDTH, FHEIGHT, HEIGHT, PIXFMT)\
       +        dprintf(FD, XPRINTF_HEAD_FMT_FMT(FFRAMES, FWIDTH, FHEIGHT), XPRINTF_HEAD_FMT_ARGS(FRAMES, WIDTH, HEIGHT, PIXFMT))
        
        #define einit_stream(...)             eninit_stream(1, __VA_ARGS__)
        #define eopen_stream(...)             enopen_stream(1, __VA_ARGS__)
       @@ -33,6 +47,8 @@
        #define eread_frame(...)              enread_frame(1, __VA_ARGS__)
        #define eread_row(...)                enread_row(1, __VA_ARGS__)
        #define esend_frames(...)             ensend_frames(1, __VA_ARGS__)
       +#define esend_rows(...)               ensend_rows(1, __VA_ARGS__)
       +#define esend_pixels(...)             ensend_pixels(1, __VA_ARGS__)
        #define esend_stream(...)             ensend_stream(1, __VA_ARGS__)
        
        #define process_stream(...)                 nprocess_stream(1, __VA_ARGS__)
       @@ -79,6 +95,8 @@ void encheck_compat(int status, const struct stream *a, const struct stream *b);
        const char *get_pixel_format(const char *specified, const char *current);
        int enread_segment(int status, struct stream *stream, void *buf, size_t n);
        size_t ensend_frames(int status, struct stream *stream, int outfd, size_t frames, const char *outfname);
       +size_t ensend_rows(int status, struct stream *stream, int outfd, size_t rows, const char *outfname);
       +size_t ensend_pixels(int status, struct stream *stream, int outfd, size_t pixels, const char *outfname);
        int ensend_stream(int status, struct stream *stream, int outfd, const char *outfname);
        
        void nprocess_stream(int status, struct stream *stream, void (*process)(struct stream *stream, size_t n));