blind

suckless command-line video editing utility
git clone git://git.suckless.org/blind
Log | Files | Refs | README | LICENSE

commit b518ce8977c0b97dd2236fc8cbc7dad1dd70511b
parent afb5da1566586f6f8f53a8b7e443b8bca70542fe
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:
MMakefile | 3+++
MTODO | 10++++++----
Asrc/blind-disperse.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/blind-split-cols.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asrc/blind-split-rows.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/stream.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/stream.h | 34++++++++++++++++++++++++++--------
7 files changed, 226 insertions(+), 14 deletions(-)

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\ 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) 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; +} 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; +} 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; +} 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) { 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));