blind

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

commit 9fa7221a87817fe41a12a9cbdaa4bac6ee67b07d
parent 1870c353eb4697290cf7f49861e5d1b8699e3cd3
Author: Mattias Andrée <maandree@kth.se>
Date:   Sun,  8 Jan 2017 08:00:53 +0100

Add vu-stack

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

Diffstat:
Msrc/util.h | 25+++++++++++++++++++++++++
Asrc/vu-stack.c | 171+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 196 insertions(+), 0 deletions(-)

diff --git a/src/util.h b/src/util.h @@ -1,5 +1,6 @@ /* See LICENSE file for copyright and license details. */ +#include <math.h> #include <stdio.h> #define ELEMENTSOF(ARRAY) (sizeof(ARRAY) / sizeof(*(ARRAY))) @@ -33,3 +34,27 @@ DEF_STR_TO_INT(toi, int, tolli, long long int) int fshut(FILE *fp, const char *fname); void enfshut(int status, FILE *fp, const char *fname); void efshut(FILE *fp, const char *fname); + +static inline double +srgb_encode(double t) +{ + double sign = 1; + if (t < 0) { + t = -t; + sign = -1; + } + t = t <= 0.0031306684425217108 ? 12.92 * t : 1.055 * pow(t, 1 / 2.4) - 0.055; + return t * sign; +} + +static inline double +srgb_decode(double t) +{ + double sign = 1; + if (t < 0) { + t = -t; + sign = -1; + } + t = t <= 0.0031306684425217108 * 12.92 ? t / 12.92 : pow((t + 0.055) / 1.055, 2.4); + return t * sign; +} diff --git a/src/vu-stack.c b/src/vu-stack.c @@ -0,0 +1,171 @@ +/* See LICENSE file for copyright and license details. */ +#include "arg.h" +#include "util.h" + +#include <fcntl.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +struct file +{ + int fd; + unsigned char buf[1024]; + size_t ptr; + const char *file; +}; + +static unsigned int +clip(unsigned int value) +{ + return value < 0 ? 0 : value > 255 ? 255 : value; +} + +static void +blend(struct file *files, size_t n_files, size_t n) +{ + double r1, g1, b1, a1; + double r2, g2, b2, a2; + size_t i, j; + for (i = 0; i < n; i += 4) { + r1 = srgb_decode(files->buf[i + 0] / 255.); + g1 = srgb_decode(files->buf[i + 1] / 255.); + b1 = srgb_decode(files->buf[i + 2] / 255.); + a1 = files->buf[i + 3] / 255.; + for (j = 1; j < n_files;) { + r2 = srgb_decode(files[j].buf[i + 0] / 255.); + g2 = srgb_decode(files[j].buf[i + 1] / 255.); + b2 = srgb_decode(files[j].buf[i + 2] / 255.); + a2 = files[j].buf[i + 3] / 255.; + a2 /= ++j; + r1 = r1 * a1 * (1 - a2) + r2 * a2; + g1 = g1 * a1 * (1 - a2) + g2 * a2; + b1 = b1 * a1 * (1 - a2) + b2 * a2; + a1 = a1 * (1 - a2) + a2; + } + r1 = srgb_encode(r1) * 255 + 0.5; + g1 = srgb_encode(g1) * 255 + 0.5; + b1 = srgb_encode(b1) * 255 + 0.5; + a1 = a1 * 255 * 0.5; + files->buf[i + 0] = clip((unsigned int)r1); + files->buf[i + 1] = clip((unsigned int)g1); + files->buf[i + 2] = clip((unsigned int)b1); + files->buf[i + 3] = clip((unsigned int)a1); + } +} + +static void +stack(struct file *files, size_t n_files, size_t n) +{ + double r1, g1, b1, a1; + double r2, g2, b2, a2; + size_t i, j; + for (i = 0; i < n; i += 4) { + r1 = srgb_decode(files->buf[i + 0] / 255.); + g1 = srgb_decode(files->buf[i + 1] / 255.); + b1 = srgb_decode(files->buf[i + 2] / 255.); + a1 = files->buf[i + 3] / 255.; + for (j = 1; j < n_files; j++) { + r2 = srgb_decode(files[j].buf[i + 0] / 255.); + g2 = srgb_decode(files[j].buf[i + 1] / 255.); + b2 = srgb_decode(files[j].buf[i + 2] / 255.); + a2 = files[j].buf[i + 3] / 255.; + r1 = r1 * a1 * (1 - a2) + r2 * a2; + g1 = g1 * a1 * (1 - a2) + g2 * a2; + b1 = b1 * a1 * (1 - a2) + b2 * a2; + a1 = a1 * (1 - a2) + a2; + } + r1 = srgb_encode(r1) * 255 + 0.5; + g1 = srgb_encode(g1) * 255 + 0.5; + b1 = srgb_encode(b1) * 255 + 0.5; + a1 = a1 * 255 * 0.5; + files->buf[i + 0] = clip((unsigned int)r1); + files->buf[i + 1] = clip((unsigned int)g1); + files->buf[i + 2] = clip((unsigned int)b1); + files->buf[i + 3] = clip((unsigned int)a1); + } +} + +static void +usage(void) +{ + eprintf("usage: %s [-b] bottom-image ... top-image\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + struct file *files; + size_t n_files; + int blend_flag = 0; + size_t i, j, n; + ssize_t r; + size_t closed; + + ARGBEGIN { + case 'b': + blend_flag = 1; + break; + default: + usage(); + } ARGEND; + + if (argc < 2) + usage(); + + n_files = (size_t)argc; + files = calloc(n_files, sizeof(*files)); + if (!files) + eprintf("calloc:"); + + for (i = 0; i < n_files; i++) { + files[i].fd = open(argv[i], O_RDONLY); + if (files[i].fd < 0) + eprintf("open %s:", argv[i]); + files[i].file = argv[i]; + } + + while (n_files) { + n = SIZE_MAX; + for (i = 0; i < n_files; i++) { + r = read(files[i].fd, files[i].buf + files[i].ptr, sizeof(files[i].buf) - files[i].ptr); + if (r < 0) { + eprintf("read %s:", files[i].file); + } else if (r == 0) { + close(files[i].fd); + files[i].fd = -1; + } else { + files[i].ptr += (size_t)r; + } + if (files[i].ptr && files[i].ptr < n) + n = files[i].ptr; + } + n -= n & 3; + + (blend_flag ? blend : stack)(files, n_files, n); + for (j = 0; j < n;) { + r = write(STDOUT_FILENO, files->buf + j, n - j); + if (r < 0) + eprintf("write <stdout>:"); + j += (size_t)r; + } + + closed = SIZE_MAX; + for (i = 0; i < n_files; i++) { + memmove(files[i].buf, files[i].buf + n, files[i].ptr -= n); + if (files[i].ptr < 4 && files[i].fd < 0 && closed == SIZE_MAX) + closed = i; + } + if (closed != SIZE_MAX) { + for (i = (j = closed) + 1; i < n_files; i++) + if (files[i].ptr < 4 && files[i].fd < 0) + files[j++] = files[i]; + n_files = j; + } + } + + return 0; +}