blind

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

blind-mosaic.c (4819B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include "common.h"
      3 
      4 USAGE("[-xy] mosaic-stream")
      5 
      6 static int tiled_x = 0;
      7 static int tiled_y = 0;
      8 
      9 #define TEST(X, Y)\
     10 	(!*(size_t *)(img + (Y) * mosaic->width + (X)) &&\
     11 	 mos[(Y) * mosaic->width + (X)][0] == ch1 &&\
     12 	 mos[(Y) * mosaic->width + (X)][1] == ch2 &&\
     13 	 mos[(Y) * mosaic->width + (X)][2] == ch3 &&\
     14 	 mos[(Y) * mosaic->width + (X)][3] == ch4)
     15 
     16 #define SEARCH(TYPE, SEARCH_FUNCTION)\
     17 	do {\
     18 		typedef TYPE pixel_t[4];\
     19 		\
     20 		pixel_t *restrict mos = (pixel_t *)mbuf;\
     21 		pixel_t *restrict img = (pixel_t *)output;\
     22 		size_t n, s, e, w;\
     23 		\
     24 		*(size_t *)(img + y * mosaic->width + x) = index;\
     25 		\
     26 		n = y ? y - 1 : tiled_y ? mosaic->height - 1 : y;\
     27 		s = y <= mosaic->height ? y + 1 : tiled_y ? 0 : y;\
     28 		w = x ? x - 1 : tiled_x ? mosaic->width - 1 : x;\
     29 		e = x <= mosaic->width ? x + 1 : tiled_x ? 0 : x;\
     30 		\
     31 		if (TEST(x, n)) SEARCH_FUNCTION(output, mbuf, mosaic, x, n, index, ch1, ch2, ch3, ch4);\
     32 		if (TEST(x, s)) SEARCH_FUNCTION(output, mbuf, mosaic, x, s, index, ch1, ch2, ch3, ch4);\
     33 		if (TEST(e, y)) SEARCH_FUNCTION(output, mbuf, mosaic, e, y, index, ch1, ch2, ch3, ch4);\
     34 		if (TEST(w, y)) SEARCH_FUNCTION(output, mbuf, mosaic, w, y, index, ch1, ch2, ch3, ch4);\
     35 	} while (0)\
     36 
     37 #define PROCESS(TYPE, SEARCH_FUNCTION)\
     38 	do {\
     39 		typedef TYPE pixel_t[4];\
     40 		\
     41 		static pixel_t *avg = NULL;\
     42 		static TYPE *cnt = NULL;\
     43 		static size_t size = 0;\
     44 		\
     45 		pixel_t *restrict clr = (pixel_t *)cbuf;\
     46 		pixel_t *restrict mos = (pixel_t *)mbuf;\
     47 		pixel_t *img = (pixel_t *)output;\
     48 		size_t index = 0;\
     49 		size_t x, y, i;\
     50 		\
     51 		memset(img, 0, mosaic->frame_size);\
     52 		\
     53 		for (y = 0; y < mosaic->height; y++)\
     54 			for (x = 0; x < mosaic->width; x++)\
     55 				if (!*(size_t *)(img + y * mosaic->width + x))\
     56 					SEARCH_FUNCTION(img, mos, mosaic, x, y, ++index,\
     57 					                mos[y * mosaic->width + x][0],\
     58 					                mos[y * mosaic->width + x][1],\
     59 					                mos[y * mosaic->width + x][2],\
     60 					                mos[y * mosaic->width + x][3]);\
     61 		\
     62 		if (index > size) {\
     63 			size = index;\
     64 			avg = erealloc2(avg, size, sizeof(*avg));\
     65 			cnt = erealloc2(cnt, size, sizeof(*cnt));\
     66 		}\
     67 		memset(avg, 0, index * sizeof(*avg));\
     68 		memset(cnt, 0, index * sizeof(*cnt));\
     69 		\
     70 		for (y = 0; y < mosaic->height; y++) {\
     71 			for (x = 0; x < mosaic->width; x++) {\
     72 				i = y * mosaic->width + x;\
     73 				index = *(size_t *)(img + i) - 1;\
     74 				cnt[index] += (TYPE)1;\
     75 				avg[index][0] *= (cnt[index] - (TYPE)1) / cnt[index];\
     76 				avg[index][1] *= (cnt[index] - (TYPE)1) / cnt[index];\
     77 				avg[index][2] *= (cnt[index] - (TYPE)1) / cnt[index];\
     78 				avg[index][3] *= (cnt[index] - (TYPE)1) / cnt[index];\
     79 				avg[index][3] += clr[i][3] /= cnt[index];\
     80 				avg[index][0] += clr[i][0] *= clr[i][3];\
     81 				avg[index][1] += clr[i][1] *= clr[i][3];\
     82 				avg[index][2] += clr[i][2] *= clr[i][3];\
     83 			}\
     84 		}\
     85 		\
     86 		for (i = 0; i < index; i++) {\
     87 			if (avg[i][3]) {\
     88 				avg[i][0] /= avg[i][3];\
     89 				avg[i][1] /= avg[i][3];\
     90 				avg[i][2] /= avg[i][3];\
     91 			}\
     92 		}\
     93 		\
     94 		for (y = 0; y < mosaic->height; y++) {\
     95 			for (x = 0; x < mosaic->width; x++) {\
     96 				i = y * mosaic->width + x;\
     97 				index = *(size_t *)(img + i) - 1;\
     98 				img[i][0] = avg[index][0];\
     99 				img[i][1] = avg[index][1];\
    100 				img[i][2] = avg[index][2];\
    101 				img[i][3] = avg[index][3];\
    102 			}\
    103 		}\
    104 		\
    105 		(void) colour;\
    106 	} while (0)
    107 
    108 static void
    109 search_lf(void *restrict output, void *restrict mbuf, struct stream *mosaic,
    110 	  size_t x, size_t y, size_t index, double ch1, double ch2, double ch3, double ch4)
    111 {
    112 	SEARCH(double, search_lf);
    113 }
    114 
    115 static void
    116 search_f(void *restrict output, void *restrict mbuf, struct stream *mosaic,
    117 	 size_t x, size_t y, size_t index, float ch1, float ch2, float ch3, float ch4)
    118 {
    119 	SEARCH(float, search_f);
    120 }
    121 
    122 static void
    123 process_lf(char *restrict output, char *restrict cbuf, char *restrict mbuf,
    124 	   struct stream *colour, struct stream *mosaic)
    125 {
    126 	PROCESS(double, search_lf);
    127 }
    128 
    129 static void
    130 process_f(char *restrict output, char *restrict cbuf, char *restrict mbuf,
    131 	  struct stream *colour, struct stream *mosaic)
    132 {
    133 	PROCESS(float, search_f);
    134 }
    135 
    136 int
    137 main(int argc, char *argv[])
    138 {
    139 	struct stream colour, mosaic;
    140 	void (*process)(char *restrict output, char *restrict cbuf, char *restrict mbuf,
    141 			struct stream *colour, struct stream *mosaic);
    142 
    143 	ARGBEGIN {
    144 	case 'x':
    145 		tiled_x = 1;
    146 		break;
    147 	case 'y':
    148 		tiled_y = 1;
    149 		break;
    150 	default:
    151 		usage();
    152 	} ARGEND;
    153 
    154 	if (argc != 1)
    155 		usage();
    156 
    157 	eopen_stream(&colour, NULL);
    158 	eopen_stream(&mosaic, argv[0]);
    159 
    160 	SELECT_PROCESS_FUNCTION(&colour);
    161 	CHECK_ALPHA(&colour);
    162 	CHECK_N_CHAN(&colour, 4, 4);
    163 
    164 	echeck_compat(&colour, &mosaic);
    165 	fprint_stream_head(stdout, &colour);
    166 	efflush(stdout, "<stdout>");
    167 	process_each_frame_two_streams(&colour, &mosaic, STDOUT_FILENO, "<stdout>", process);
    168 	return 0;
    169 }