blind

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

blind-apply-kernel.c (3898B)


      1 /* See LICENSE file for copyright and license details. */
      2 #ifndef TYPE
      3 #include "common.h"
      4 
      5 USAGE("[-apPxy] kernel-stream")
      6 
      7 static int no_alpha = 0;
      8 static int dont_premultiply = 0;
      9 static int per_pixel = 0;
     10 static int wrap_x = 0;
     11 static int wrap_y = 0;
     12 static size_t kern_w;
     13 static size_t kern_h;
     14 
     15 #define FILE "blind-apply-kernel.c"
     16 #include "define-functions.h"
     17 
     18 int
     19 main(int argc, char *argv[])
     20 {
     21 	struct stream colour, kernel;
     22 	void (*process)(struct stream *colour, struct stream *kernel);
     23 	size_t tmp;
     24 
     25 	ARGBEGIN {
     26 	case 'a':
     27 		no_alpha = 1;
     28 		break;
     29 	case 'p':
     30 		per_pixel = 1;
     31 		break;
     32 	case 'P':
     33 		dont_premultiply = 1;
     34 		break;
     35 	case 'x':
     36 		wrap_x = 1;
     37 		break;
     38 	case 'y':
     39 		wrap_y = 1;
     40 		break;
     41 	default:
     42 		usage();
     43 	} ARGEND;
     44 
     45 	if (argc != 1)
     46 		usage();
     47 
     48 	eopen_stream(&colour, NULL);
     49 	eopen_stream(&kernel, argv[0]);
     50 
     51 	SELECT_PROCESS_FUNCTION(&colour);
     52 	CHECK_ALPHA_CHAN(&colour);
     53 	CHECK_N_CHAN(&colour, 4, 4);
     54 	if (colour.encoding != kernel.encoding || colour.n_chan != kernel.n_chan)
     55 		eprintf("videos use incompatible pixel formats");
     56 	if (per_pixel && !(kernel.width % colour.width || kernel.height % colour.height))
     57 		eprintf("-p is specified but the dimensions of kernel-stream "
     58 		        "are not multiples of the dimensions of stdin.");
     59 
     60 	kern_w = per_pixel ? kernel.width  / colour.width  : kernel.width;
     61 	kern_h = per_pixel ? kernel.height / colour.height : kernel.height;
     62 
     63 	tmp = kernel.height, kernel.height = kern_h;
     64 	echeck_dimensions(&colour, WIDTH | HEIGHT, NULL);
     65 	echeck_dimensions(&kernel, WIDTH | HEIGHT, NULL);
     66 	kernel.height = tmp;
     67 
     68 	fprint_stream_head(stdout, &colour);
     69 	efflush(stdout, "<stdout>");
     70 	process(&colour, &kernel);
     71 	return 0;
     72 }
     73 
     74 #else
     75 
     76 static void
     77 PROCESS(struct stream *colour, struct stream *kernel)
     78 {
     79 	TYPE *out, *clr, *krn, *kern, *pix;
     80 	size_t i, x, y, n, x2, y2;
     81 	ssize_t cx, cy, xoff, yoff;
     82 
     83 	out = emalloc(colour->frame_size);
     84 	clr = emalloc(colour->frame_size);
     85 	krn = emalloc2(kern_h, kernel->row_size);
     86 
     87 	xoff = (ssize_t)(kern_w / 2);
     88 	yoff = (ssize_t)(kern_h / 2);
     89 
     90 	n = colour->width * colour->height * colour->n_chan;
     91 	while (eread_frame(colour, clr)) {
     92 		/* premultiply */
     93 		if (!no_alpha && !dont_premultiply) {
     94 			for (i = 0; i < n; i += 4) {
     95 				clr[i + 0] *= clr[i + 3];
     96 				clr[i + 1] *= clr[i + 3];
     97 				clr[i + 2] *= clr[i + 3];
     98 			}
     99 		}
    100 
    101 		/* apply kernel */
    102 		memset(out, 0, colour->frame_size);
    103 		pix = out;
    104 		for (y = 0; y < colour->height; y++) {
    105 			if ((!y || per_pixel) && !eread_segment(kernel, krn, kern_h * kernel->row_size))
    106 				goto done;
    107 			for (x = 0; x < colour->width; x++, pix += colour->n_chan) {
    108 				kern = per_pixel ? (krn + x * kern_w * kernel->n_chan) : krn;
    109 				for (y2 = 0; y2 < kern_h; y2++, kern += kernel->width * kernel->n_chan) {
    110 					cy = (ssize_t)(y + y2) - yoff;
    111 					if (cy < 0 || (size_t)cy >= colour->height) {
    112 						if (!wrap_y)
    113 							continue;
    114 						cy %= (ssize_t)(colour->height);
    115 						if (cy < 0)
    116 							cy += (ssize_t)(colour->height);
    117 					}
    118 					for (x2 = 0; x2 < kern_w; x2++) {
    119 						cx = (ssize_t)(x + x2) - xoff;
    120 						if (cx < 0 || (size_t)cx >= colour->width) {
    121 							if (!wrap_x)
    122 								continue;
    123 							cx %= (ssize_t)(colour->width);
    124 							if (cx < 0)
    125 								cx += (ssize_t)(colour->width);
    126 						}
    127 						for (i = 0; i < colour->n_chan; i++)
    128 							pix[i] += kern[x2 * kernel->n_chan + i] *
    129 							          clr[((size_t)cy * colour->width + (size_t)cx) * colour->n_chan + i];
    130 					}
    131 				}
    132 			}
    133 		}
    134 
    135 		/* unpremultiply */
    136 		if (!dont_premultiply) {
    137 			for (i = 0; i < n; i += 4) {
    138 				if (out[i + 3]) {
    139 					out[i + 0] /= out[i + 3];
    140 					out[i + 1] /= out[i + 3];
    141 					out[i + 2] /= out[i + 3];
    142 				}
    143 			}
    144 		}
    145 
    146 		/* ensure video is opaque if -a was used */
    147 		if (no_alpha)
    148 			for (i = 0; i < n; i += 4)
    149 				out[i + 3] = 1;
    150 
    151 		/* output video */
    152 		ewriteall(STDOUT_FILENO, out, colour->frame_size, "<stdout>");
    153 	}
    154 done:
    155 
    156 	free(out);
    157 	free(clr);
    158 	free(krn);
    159 }
    160 
    161 #endif