Add blind-mosaic - 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 <[email protected]> | |
Date: Thu, 13 Jul 2017 17:40:34 +0200 | |
Add blind-mosaic | |
Signed-off-by: Mattias Andrée <[email protected]> | |
Diffstat: | |
M Makefile | 1 + | |
M README | 7 +++++-- | |
M man/blind-gauss-blur.1 | 2 +- | |
M man/blind-hexagon-tessellation.1 | 3 ++- | |
A man/blind-mosaic.1 | 36 +++++++++++++++++++++++++++++… | |
M man/blind-rectangle-tessellation.1 | 3 ++- | |
M man/blind-repeat-tessellation.1 | 3 ++- | |
M man/blind-triangle-tessellation.1 | 3 ++- | |
M man/blind.7 | 3 +++ | |
A 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 < [email protected] > | |
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 < [email protected] > | |
diff --git a/man/blind-rectangle-tessellation.1 b/man/blind-rectangle-tessellat… | |
@@ -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 < [email protected] > | |
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 < [email protected] > | |
diff --git a/man/blind-triangle-tessellation.1 b/man/blind-triangle-tessellatio… | |
@@ -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 < [email protected] > | |
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, in… | |
+ if (TEST(x, s)) SEARCH_FUNCTION(output, mbuf, mosaic, x, s, in… | |
+ if (TEST(e, y)) SEARCH_FUNCTION(output, mbuf, mosaic, e, y, in… | |
+ if (TEST(w, y)) SEARCH_FUNCTION(output, mbuf, mosaic, w, y, in… | |
+ } 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… | |
+ mos[y * mosaic->width … | |
+ mos[y * mosaic->width … | |
+ mos[y * mosaic->width … | |
+ mos[y * mosaic->width … | |
+ \ | |
+ 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[… | |
+ avg[index][1] *= (cnt[index] - (TYPE)1) / cnt[… | |
+ avg[index][2] *= (cnt[index] - (TYPE)1) / cnt[… | |
+ avg[index][3] *= (cnt[index] - (TYPE)1) / cnt[… | |
+ 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… | |
+{ | |
+ 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,… | |
+{ | |
+ 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 *rest… | |
+ 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… | |
+ | |
+ echeck_compat(&colour, &mosaic); | |
+ | |
+ fprint_stream_head(stdout, &colour); | |
+ efflush(stdout, "<stdout>"); | |
+ process_each_frame_two_streams(&colour, &mosaic, STDOUT_FILENO, "<stdo… | |
+ | |
+ return 0; | |
+} |