/* See LICENSE file for copyright and license details. */ #include "common.h" USAGE("palette-stream") static double (*compare)(double x1, double y1, double z1, double a1, double x2, double y2, double z2, double a2); /* TODO add more formulae: https://en.wikipedia.org/wiki/Color_difference */ static double distance_xyz(double x1, double y1, double z1, double a1, double x2, double y2, double z2, double a2) { x2 -= x1, x2 *= x2; y2 -= y1, y2 *= y2; z2 -= z1, z2 *= z2; a2 -= a1, a2 *= a2; return sqrt(x2 + y2 + z2 + a2); } #define PROCESS(TYPE, SUFFIX)\ static void\ process_##SUFFIX(struct stream *stream, struct stream *palette, char *pal)\ {\ size_t i, j, n, m;\ size_t palsiz = palette->width * palette->height;\ size_t best = 0;\ TYPE x, y, z, a, lx = 0, ly = 0, lz = 0, la = 0;\ TYPE cx, cy, cz, ca;\ double distance, best_distance = 0;\ while (eread_frame(palette, pal)) {\ m = stream->frame_size;\ do {\ n = MIN(stream->ptr, m) / stream->pixel_size;\ for (i = 0; i < n; i++) {\ x = ((TYPE *)(stream->buf + i * stream->pixel_size))[0];\ y = ((TYPE *)(stream->buf + i * stream->pixel_size))[1];\ z = ((TYPE *)(stream->buf + i * stream->pixel_size))[2];\ a = ((TYPE *)(stream->buf + i * stream->pixel_size))[3];\ if ((!i && m == stream->frame_size) || x != lx || y != ly || z != lz || a != la) {\ for (j = 0; j < palsiz; j++) {\ cx = ((TYPE *)(pal + j * stream->pixel_size))[0];\ cy = ((TYPE *)(pal + j * stream->pixel_size))[1];\ cz = ((TYPE *)(pal + j * stream->pixel_size))[2];\ ca = ((TYPE *)(pal + j * stream->pixel_size))[3];\ distance = compare((double)x, (double)y, (double)z, (double)a,\ (double)cx, (double)cy, (double)cz, (double)ca);\ if (!j || distance < best_distance) {\ best_distance = distance;\ best = j;\ }\ }\ lx = x, ly = y, lz = z, la = a;\ }\ memcpy(stream->buf + i * stream->pixel_size,\ pal + best * stream->pixel_size,\ stream->pixel_size);\ }\ m -= n *= stream->pixel_size;\ ewriteall(STDOUT_FILENO, stream->buf, n, "");\ memmove(stream->buf, stream->buf + n, stream->ptr -= n);\ } while (m && eread_stream(stream, SIZE_MAX));\ if (m)\ eprintf("%s: incomplete frame\n", stream->file);\ }\ } PROCESS(double, lf) PROCESS(float, f) int main(int argc, char *argv[]) { struct stream stream, palette; void (*process)(struct stream *stream, struct stream *palette, char *pal); char *pal; compare = distance_xyz; UNOFLAGS(argc != 1); eopen_stream(&stream, NULL); eopen_stream(&palette, argv[0]); if (!strcmp(stream.pixfmt, "xyza")) process = process_lf; else if (!strcmp(stream.pixfmt, "xyza f")) process = process_f; else eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt); if (strcmp(stream.pixfmt, palette.pixfmt)) eprintf("videos use incompatible pixel formats\n"); echeck_dimensions(&stream, WIDTH | HEIGHT, NULL); echeck_dimensions(&palette, WIDTH | HEIGHT, NULL); pal = emalloc(palette.frame_size); fprint_stream_head(stdout, &stream); efflush(stdout, ""); process(&stream, &palette, pal); free(pal); return 0; }