blind

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

commit 38dfe2510b19c25901e9db08426319c21fabdf95
parent 2db9b5387a659884cb98284c060a27895c64ae15
Author: Mattias Andrée <maandree@kth.se>
Date:   Thu, 13 Jul 2017 17:40:34 +0200

Add blind-mosaic

Signed-off-by: Mattias Andrée <maandree@kth.se>

Diffstat:
Makefile | 1+
README | 7+++++--
man/blind-gauss-blur.1 | 2+-
man/blind-hexagon-tessellation.1 | 3++-
man/blind-mosaic.1 | 36++++++++++++++++++++++++++++++++++++
man/blind-rectangle-tessellation.1 | 3++-
man/blind-repeat-tessellation.1 | 3++-
man/blind-triangle-tessellation.1 | 3++-
man/blind.7 | 3+++
src/blind-mosaic.c | 174+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10 files changed, 228 insertions(+), 7 deletions(-)

diff --git a/Makefile b/Makefile @@ -41,6 +41,7 @@ BIN =\ blind-invert-luma\ blind-linear-gradient\ blind-make-kernel\ + blind-mosaic\ blind-next-frame\ blind-norm\ blind-quaternion-product\ diff --git a/README b/README @@ -111,11 +111,14 @@ UTILITIES blind-invert-luma(1) Invert the luminosity of a video + blind-linear-gradient(1) + Generate a video with a linear gradient + blind-make-kernel(1) Create a custom convolution matrix - blind-linear-gradient(1) - Generate a video with a linear gradient + blind-mosaic(1) + Redraw each frame in video as a mosaic blind-next-frame(1) Extracts the next frame from a video diff --git a/man/blind-gauss-blur.1 b/man/blind-gauss-blur.1 @@ -73,7 +73,7 @@ Use the Y value (multiplied by the alpha value) from .I sd-stream as the standard deviation all channels. .SH REQUIREMENTS -.B blind-compress +.B blind-gauss-blur requires enough free memory to load three full frames into memory. A frame requires 32 bytes per pixel it contains. .SH SEE ALSO diff --git a/man/blind-hexagon-tessellation.1 b/man/blind-hexagon-tessellation.1 @@ -28,7 +28,8 @@ for more information. .BR blind-triangle-tessellation (1), .BR blind-repeat-tessellation (1), .BR blind-get-colours (1), -.BR blind-apply-palette (1) +.BR blind-apply-palette (1), +.BR blind-mosaic (1) .SH AUTHORS Mattias Andrée .RI < maandree@kth.se > diff --git a/man/blind-mosaic.1 b/man/blind-mosaic.1 @@ -0,0 +1,36 @@ +.TH BLIND-MOSAIC 1 blind +.SH NAME +blind-mosaic - Redraw each frame in video as a mosaic +.SH SYNOPSIS +.B blind-mosaic +[-xy] +.I mosaic-stream +.SH DESCRIPTION +.B blind-mosaic +reads a video from stdin and a mosaic pattern video from +.IR mosaic-stream . +The video is printed to stdout, with each frame redraw in +as a mosaic with the pattern found in the same frame in +.IR mosaic-stream . +.SH OPTIONS +.TP +.B -x +When encountering the left or right edge of the video, +wrap around to the opposite edge. +.TP +.B -y +When encountering the upper or lower edge of the video, +wrap around to the opposite edge. +.SH REQUIREMENTS +.B blind-mosaic +requires enough free memory to load three full frames into +memory. A frame requires 32 bytes per pixel it contains. +.SH SEE ALSO +.BR blind (7), +.BR blind-hexagon-tessellation (1), +.BR blind-rectangle-tessellation (1), +.BR blind-triangle-tessellation (1), +.BR blind-repeat-tessellation (1) +.SH AUTHORS +Mattias Andrée +.RI < maandree@kth.se > diff --git a/man/blind-rectangle-tessellation.1 b/man/blind-rectangle-tessellation.1 @@ -30,7 +30,8 @@ for more information. .BR blind-triangle-tessellation (1), .BR blind-repeat-tessellation (1), .BR blind-get-colours (1), -.BR blind-apply-palette (1) +.BR blind-apply-palette (1), +.BR blind-mosaic (1) .SH AUTHORS Mattias Andrée .RI < maandree@kth.se > diff --git a/man/blind-repeat-tessellation.1 b/man/blind-repeat-tessellation.1 @@ -33,7 +33,8 @@ bytes per pixel it contains. .BR blind-apply-palette (1), .BR blind-hexagon-tessellation (1), .BR blind-rectangle-tessellation (1), -.BR blind-triangle-tessellation (1) +.BR blind-triangle-tessellation (1), +.BR blind-mosaic (1) .SH AUTHORS Mattias Andrée .RI < maandree@kth.se > diff --git a/man/blind-triangle-tessellation.1 b/man/blind-triangle-tessellation.1 @@ -33,7 +33,8 @@ for more information. .BR blind-triangle-tessellation (1), .BR blind-repeat-tessellation (1), .BR blind-get-colours (1), -.BR blind-apply-palette (1) +.BR blind-apply-palette (1), +.BR blind-mosaic (1) .SH AUTHORS Mattias Andrée .RI < maandree@kth.se > diff --git a/man/blind.7 b/man/blind.7 @@ -133,6 +133,9 @@ Generate a video with a linear gradient .BR blind-make-kernel (1) Create a custom convolution matrix .TP +.BR blind-mosaic (1) +Redraw each frame in video as a mosaic +.TP .BR blind-next-frame (1) Extracts the next frame from a video .TP diff --git a/src/blind-mosaic.c b/src/blind-mosaic.c @@ -0,0 +1,174 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +USAGE("[-xy] mosaic-stream") + +static int tiled_x = 0; +static int tiled_y = 0; + +#define TEST(X, Y)\ + (!*(size_t *)(img + (Y) * mosaic->width + (X)) &&\ + mos[(Y) * mosaic->width + (X)][0] == ch1 &&\ + mos[(Y) * mosaic->width + (X)][1] == ch2 &&\ + mos[(Y) * mosaic->width + (X)][2] == ch3 &&\ + mos[(Y) * mosaic->width + (X)][3] == ch4) + +#define SEARCH(TYPE, SEARCH_FUNCTION)\ + do {\ + typedef TYPE pixel_t[4];\ + \ + pixel_t *restrict mos = (pixel_t *)mbuf;\ + pixel_t *restrict img = (pixel_t *)output;\ + size_t n, s, e, w;\ + \ + *(size_t *)(img + y * mosaic->width + x) = index;\ + \ + n = y ? y - 1 : tiled_y ? mosaic->height - 1 : y;\ + s = y <= mosaic->height ? y + 1 : tiled_y ? 0 : y;\ + w = x ? x - 1 : tiled_x ? mosaic->width - 1 : x;\ + e = x <= mosaic->width ? x + 1 : tiled_x ? 0 : x;\ + \ + if (TEST(x, n)) SEARCH_FUNCTION(output, mbuf, mosaic, x, n, index, ch1, ch2, ch3, ch4);\ + if (TEST(x, s)) SEARCH_FUNCTION(output, mbuf, mosaic, x, s, index, ch1, ch2, ch3, ch4);\ + if (TEST(e, y)) SEARCH_FUNCTION(output, mbuf, mosaic, e, y, index, ch1, ch2, ch3, ch4);\ + if (TEST(w, y)) SEARCH_FUNCTION(output, mbuf, mosaic, w, y, index, ch1, ch2, ch3, ch4);\ + } while (0)\ + +#define PROCESS(TYPE, SEARCH_FUNCTION)\ + do {\ + typedef TYPE pixel_t[4];\ + \ + static pixel_t *avg = NULL;\ + static TYPE *cnt = NULL;\ + static size_t size = 0;\ + \ + pixel_t *restrict clr = (pixel_t *)cbuf;\ + pixel_t *restrict mos = (pixel_t *)mbuf;\ + pixel_t *img = (pixel_t *)output;\ + size_t index = 0;\ + size_t x, y, i;\ + \ + memset(img, 0, mosaic->frame_size);\ + \ + for (y = 0; y < mosaic->height; y++)\ + for (x = 0; x < mosaic->width; x++)\ + if (!*(size_t *)(img + y * mosaic->width + x))\ + SEARCH_FUNCTION(img, mos, mosaic, x, y, ++index,\ + mos[y * mosaic->width + x][0],\ + mos[y * mosaic->width + x][1],\ + mos[y * mosaic->width + x][2],\ + mos[y * mosaic->width + x][3]);\ + \ + if (index > size) {\ + size = index;\ + avg = erealloc2(avg, size, sizeof(*avg));\ + cnt = erealloc2(cnt, size, sizeof(*cnt));\ + }\ + memset(avg, 0, index * sizeof(*avg));\ + memset(cnt, 0, index * sizeof(*cnt));\ + \ + for (y = 0; y < mosaic->height; y++) {\ + for (x = 0; x < mosaic->width; x++) {\ + i = y * mosaic->width + x;\ + index = *(size_t *)(img + i) - 1;\ + cnt[index] += (TYPE)1;\ + avg[index][0] *= (cnt[index] - (TYPE)1) / cnt[index];\ + avg[index][1] *= (cnt[index] - (TYPE)1) / cnt[index];\ + avg[index][2] *= (cnt[index] - (TYPE)1) / cnt[index];\ + avg[index][3] *= (cnt[index] - (TYPE)1) / cnt[index];\ + avg[index][3] += clr[i][3] /= cnt[index];\ + avg[index][0] += clr[i][0] *= clr[i][3];\ + avg[index][1] += clr[i][1] *= clr[i][3];\ + avg[index][2] += clr[i][2] *= clr[i][3];\ + }\ + }\ + \ + for (i = 0; i < index; i++) {\ + if (avg[i][3]) {\ + avg[i][0] /= avg[i][3];\ + avg[i][1] /= avg[i][3];\ + avg[i][2] /= avg[i][3];\ + }\ + }\ + \ + for (y = 0; y < mosaic->height; y++) {\ + for (x = 0; x < mosaic->width; x++) {\ + i = y * mosaic->width + x;\ + index = *(size_t *)(img + i) - 1;\ + img[i][0] = avg[index][0];\ + img[i][1] = avg[index][1];\ + img[i][2] = avg[index][2];\ + img[i][3] = avg[index][3];\ + }\ + }\ + \ + (void) colour;\ + } while (0) + +static void +search_lf(void *restrict output, void *restrict mbuf, struct stream *mosaic, + size_t x, size_t y, size_t index, double ch1, double ch2, double ch3, double ch4) +{ + SEARCH(double, search_lf); +} + +static void +search_f(void *restrict output, void *restrict mbuf, struct stream *mosaic, + size_t x, size_t y, size_t index, double ch1, double ch2, double ch3, double ch4) +{ + SEARCH(float, search_f); +} + +static void +process_lf(char *restrict output, char *restrict cbuf, char *restrict mbuf, + struct stream *colour, struct stream *mosaic) +{ + PROCESS(double, search_lf); +} + +static void +process_f(char *restrict output, char *restrict cbuf, char *restrict mbuf, + struct stream *colour, struct stream *mosaic) +{ + PROCESS(float, search_f); +} + +int +main(int argc, char *argv[]) +{ + struct stream colour, mosaic; + void (*process)(char *restrict output, char *restrict cbuf, char *restrict mbuf, + struct stream *colour, struct stream *mosaic); + + ARGBEGIN { + case 'x': + tiled_x = 1; + break; + case 'y': + tiled_y = 1; + break; + default: + usage(); + } ARGEND; + + if (argc != 1) + usage(); + + eopen_stream(&colour, NULL); + eopen_stream(&mosaic, argv[0]); + + if (!strcmp(colour.pixfmt, "xyza")) + process = process_lf; + else if (!strcmp(colour.pixfmt, "xyza f")) + process = process_f; + else + eprintf("pixel format %s is not supported, try xyza\n", colour.pixfmt); + + echeck_compat(&colour, &mosaic); + + fprint_stream_head(stdout, &colour); + efflush(stdout, "<stdout>"); + process_each_frame_two_streams(&colour, &mosaic, STDOUT_FILENO, "<stdout>", process); + + return 0; +}