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:
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;
+}