Add blind-mosaic - blind - suckless command-line video editing utility
 (HTM) git clone git://git.suckless.org/blind
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 38dfe2510b19c25901e9db08426319c21fabdf95
 (DIR) parent 2db9b5387a659884cb98284c060a27895c64ae15
 (HTM) 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:
         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(-)
       ---
 (DIR) 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\
 (DIR) 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
 (DIR) 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
 (DIR) 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 >
 (DIR) 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 >
 (DIR) 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 >
 (DIR) 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 >
 (DIR) 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 >
 (DIR) 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
 (DIR) 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;
       +}