Introduction
Introduction Statistics Contact Development Disclaimer Help
blind-gauss-blur.c - blind - suckless command-line video editing utility
git clone git://git.suckless.org/blind
Log
Files
Refs
README
LICENSE
---
blind-gauss-blur.c (10542B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include "common.h"
3
4 USAGE("[-j jobs] [-s spread | -s 'auto'] [-acghvy] sd-stream")
5
6 static int chroma = 0;
7 static int noalpha = 0;
8 static int glow = 0;
9 static int vertical = 0;
10 static int horizontal = 0;
11 static int measure_y_only = 0;
12 static int auto_spread = 0;
13 static size_t jobs = 1;
14 static size_t spread = 0;
15 static void *original = NULL;
16
17 /*
18 * This is not a regular simple gaussian blur implementation.
19 * This implementation is able to apply different levels of
20 * blur on different pixels. It's therefore written a bit
21 * oddly. Instead of going through each pixel and calculate
22 * the new value for each pixel, it goes through each pixel
23 * and smears it out to the other pixels.
24 */
25
26 #define BLUR_PIXEL_PROLOGUE(TYPE, DIR)\
27 if (sig[i1][3] == 0)\
28 goto no_blur_##DIR;\
29 if (chroma || measure_y_only) {\
30 k[0] = sig[i1][1] * sig[i1][3];\
31 if (auto_spread)\
32 spread = k[0] > 0 ? (size_t)(k[0] * 3 + (TYPE)0.…
33 blur[2] = blur[0] = k[0] > 0;\
34 c[0] = k[0] *= k[0] * 2, c[0] = sqrt(c[0] * (TYPE)M_PI);\
35 k[0] = 1 / -k[0], c[0] = 1 / c[0];\
36 if (chroma) {\
37 k[2] = k[0];\
38 c[2] = c[0];\
39 c[1] = k[1] = 0;\
40 blur[1] = 0;\
41 } else {\
42 k[2] = k[1] = k[0];\
43 c[2] = c[1] = c[0];\
44 blur[1] = blur[0];\
45 }\
46 } else {\
47 if (auto_spread)\
48 spread = 0;\
49 for (i = 0; i < 3; i++) {\
50 k[i] = sig[i1][i] * sig[i1][3];\
51 if (auto_spread && k[i] > 0 && spread < (size_t)…
52 spread = (size_t)(k[i] * 3 + (TYPE)0.5);\
53 blur[i] = k[i] > 0;\
54 c[i] = k[i] *= k[i] * 2, c[i] = sqrt(c[i] * (TYP…
55 k[i] = 1 / -k[i], c[i] = 1 / c[i];\
56 }\
57 }\
58 if (blur[0] + blur[1] + blur[2] == 0)\
59 goto no_blur_##DIR;\
60 if (auto_spread && spread < 1)\
61 spread = 1;
62
63 #define BLUR_PIXEL(TYPE, START, LOOP, DISTANCE)\
64 if (k[0] == k[1] && k[1] == k[2]) {\
65 START;\
66 for (LOOP) {\
67 d = (TYPE)(DISTANCE);\
68 d *= d;\
69 m = c[0] * exp(d * k[0]);\
70 img[i2][0] += clr[i1][0] * m;\
71 img[i2][1] += clr[i1][1] * m;\
72 img[i2][2] += clr[i1][2] * m;\
73 img[i2][3] += clr[i1][3] * m;\
74 }\
75 } else {\
76 blurred = 0;\
77 for (i = 0; i < 3; i++) {\
78 if (blur[i])\
79 blurred += 1;\
80 else\
81 img[i1][i] += clr[i1][i];\
82 }\
83 for (i = 0; i < 3; i++) {\
84 if (!blur[i])\
85 continue;\
86 START;\
87 for (LOOP) {\
88 d = (TYPE)(DISTANCE);\
89 d *= d;\
90 m = c[i] * exp(d * k[i]);\
91 img[i2][i] += clr[i1][i] * m;\
92 img[i2][3] += clr[i1][3] * m / (TYPE)blu…
93 }\
94 }\
95 }
96
97 #define BLUR_PIXEL_EPILOGUE(DIR)\
98 continue;\
99 no_blur_##DIR:\
100 img[i1][0] = clr[i1][0];\
101 img[i1][1] = clr[i1][1];\
102 img[i1][2] = clr[i1][2];\
103 img[i1][3] = clr[i1][3];
104
105 #define BLUR(TYPE, DIR, SETSTART, SETEND, START, LOOP, DISTANCE)\
106 do {\
107 memset(img, 0, colour->frame_size);\
108 start = 0, end = colour->height;\
109 is_master = efork_jobs(&start, &end, jobs, &children);\
110 i1 = start * colour->width;\
111 for (y1 = start; y1 < end; y1++) {\
112 for (x1 = 0; x1 < colour->width; x1++, i1++) {\
113 BLUR_PIXEL_PROLOGUE(TYPE, DIR);\
114 if (spread) {\
115 SETSTART;\
116 SETEND;\
117 }\
118 BLUR_PIXEL(TYPE, START, LOOP, DISTANCE);\
119 BLUR_PIXEL_EPILOGUE(DIR);\
120 }\
121 }\
122 ejoin_jobs(is_master, children);\
123 } while (0)
124
125 #define PROCESS(TYPE)\
126 do {\
127 typedef TYPE pixel_t[4];\
128 \
129 pixel_t *restrict clr = (pixel_t *)cbuf;\
130 pixel_t *restrict sig = (pixel_t *)sbuf;\
131 pixel_t *restrict orig = original;\
132 pixel_t *img = (pixel_t *)output;\
133 pixel_t c, k;\
134 size_t x1, y1, i1, x2, y2, i2;\
135 TYPE d, m;\
136 int i, blurred, blur[3] = {0, 0, 0};\
137 size_t start, end, x2start, x2end, y2start, y2end;\
138 int is_master;\
139 pid_t *children;\
140 \
141 y2start = x2start = 0;\
142 x2end = colour->width;\
143 y2end = colour->height;\
144 \
145 if (glow)\
146 memcpy(orig, clr, colour->frame_size);\
147 if (chroma || !noalpha) {\
148 start = 0, end = colour->height;\
149 is_master = efork_jobs(&start, &end, jobs, &chil…
150 \
151 /* premultiply alpha channel */\
152 if (!noalpha) {\
153 i1 = start * colour->width;\
154 for (y1 = start; y1 < end; y1++) {\
155 for (x1 = 0; x1 < colour->width;…
156 clr[i1][0] *= clr[i1][3]…
157 clr[i1][1] *= clr[i1][3]…
158 clr[i1][2] *= clr[i1][3]…
159 }\
160 }\
161 }\
162 \
163 /* store original image */\
164 if (glow) {\
165 i1 = start * colour->width;\
166 memcpy(orig + i1, clr + i1, (end - start…
167 }\
168 \
169 /* convert colour model */\
170 if (chroma) {\
171 i1 = start * colour->width;\
172 for (y1 = start; y1 < end; y1++) {\
173 for (x1 = 0; x1 < colour->width;…
174 clr[i1][0] = clr[i1][0] …
175 clr[i1][2] = clr[i1][2] …
176 /*
177 * Explaination:
178 * Y is the luma and (…
179 * is the chroma (acco…
180 * is the white point.
181 */\
182 }\
183 }\
184 }\
185 /* Conversion makes no difference if blur is app…
186 * parameters:
187 *
188 * Gaussian blur:
189 *
190 * ∞ ∞
191 * ⌠ ⌠ V(x,y) −((x−x�…
192 * V′ (x₀,y₀) = │ │ ────�…
193 * σ ⌡ ⌡ 2πσ²
194 * −∞ −∞
195 *
196 * With linear transformation, F:
197 *
198 * ∞ ∞
199 * ⌠ ⌠ F(V(x,y)) −((…
200 * V′ (x₀,y₀) = F⁻¹ │ │ ──�…
201 * σ ⌡ ⌡ 2πσ²
202 * −∞ −∞
203 *
204 * ∞ ∞
205 * ⌠ ⌠ ⎛V(x,y) −(…
206 * V′ (x₀,y₀) = F⁻¹ │ │ F⎜─…
207 * σ ⌡ ⌡ ⎝ 2πσ² …
208 * −∞ −∞
209 *
210 * ∞ ∞
211 * ⌠ ⌠ V(x,y) �…
212 * V′ (x₀,y₀) = (F⁻¹ ∘ F) │ │…
213 * σ ⌡ ⌡ 2πσ²
214 * −∞ −∞
215 *
216 * ∞ ∞
217 * ⌠ ⌠ V(x,y) −((x−x�…
218 * V′ (x₀,y₀) = │ │ ────�…
219 * σ ⌡ ⌡ 2πσ²
220 * −∞ −∞
221 *
222 * Just like expected, the colour space should n…
223 * result of guassian blur as long as it is line…
224 */\
225 \
226 ejoin_jobs(is_master, children);\
227 }\
228 \
229 /* blur */\
230 if (horizontal)\
231 BLUR(TYPE, horizontal,\
232 x2start = spread > x1 ? 0 : x1 - spread,\
233 x2end = spread + 1 > colour->width - x1 ? c…
234 i2 = y1 * colour->width + x2start,\
235 x2 = x2start; x2 < x2end; (x2++, i2++),\
236 (ssize_t)x1 - (ssize_t)x2);\
237 if (horizontal && vertical)\
238 memcpy(clr, img, colour->frame_size);\
239 if (vertical)\
240 BLUR(TYPE, vertical,\
241 y2start = spread > y1 ? 0 : y1 - spread,\
242 y2end = spread + 1 > colour->height - y1 ? …
243 i2 = y2start * colour->width + x1,\
244 y2 = y2start; y2 < y2end; (y2++, i2 += colo…
245 (ssize_t)y1 - (ssize_t)y2);\
246 \
247 start = 0, end = colour->height;\
248 is_master = efork_jobs(&start, &end, jobs, &children);\
249 \
250 /* convert back to CIE XYZ */\
251 if (chroma) {\
252 i1 = start * colour->width;\
253 for (y1 = start; y1 < end; y1++) {\
254 for (x1 = 0; x1 < colour->width; x1++, i…
255 img[i1][0] = (img[i1][0] + img[i…
256 img[i1][2] = (img[i1][2] + img[i…
257 }\
258 }\
259 }\
260 \
261 /* apply glow */\
262 if (glow) {\
263 i1 = start * colour->width;\
264 for (y1 = start; y1 < end; y1++) {\
265 for (x1 = 0; x1 < colour->width; x1++, i…
266 img[i1][0] += orig[i1][0];\
267 img[i1][1] += orig[i1][1];\
268 img[i1][2] += orig[i1][2];\
269 img[i1][3] += orig[i1][3];\
270 }\
271 }\
272 }\
273 \
274 /* unpremultiply alpha channel */\
275 i1 = start * colour->width;\
276 for (y1 = start; y1 < end; y1++) {\
277 for (x1 = 0; x1 < colour->width; x1++, i1++) {\
278 if (img[i1][3] != 0)\
279 continue;\
280 img[i1][0] /= img[i1][3];\
281 img[i1][1] /= img[i1][3];\
282 img[i1][2] /= img[i1][3];\
283 }\
284 }\
285 \
286 /* ensure the video if opaque if -a was used */\
287 if (noalpha) {\
288 i1 = start * colour->width;\
289 for (y1 = start; y1 < end; y1++)\
290 for (x1 = 0; x1 < colour->width; x1++, i…
291 img[i1][3] = 1;\
292 }\
293 \
294 ejoin_jobs(is_master, children);\
295 \
296 (void) sigma;\
297 } while (0)
298
299 static void
300 process_lf(char *restrict output, char *restrict cbuf, char *restrict sb…
301 struct stream *colour, struct stream *sigma)
302 {
303 PROCESS(double);
304 }
305
306 static void
307 process_f(char *restrict output, char *restrict cbuf, char *restrict sbu…
308 struct stream *colour, struct stream *sigma)
309 {
310 PROCESS(float);
311 }
312
313 int
314 main(int argc, char *argv[])
315 {
316 struct stream colour, sigma;
317 char *arg;
318 void (*process)(char *restrict output, char *restrict cbuf, char…
319 struct stream *colour, struct stream *sigma);
320
321 ARGBEGIN {
322 case 'a':
323 noalpha = 1;
324 break;
325 case 'c':
326 chroma = 1;
327 break;
328 case 'g':
329 glow = 1;
330 break;
331 case 'h':
332 horizontal = 1;
333 break;
334 case 'v':
335 vertical = 1;
336 break;
337 case 'y':
338 measure_y_only = 1;
339 break;
340 case 'j':
341 jobs = etozu_flag('j', UARGF(), 1, SHRT_MAX);
342 break;
343 case 's':
344 arg = UARGF();
345 if (!strcmp(arg, "auto"))
346 auto_spread = 1;
347 else
348 spread = etozu_flag('s', arg, 1, SIZE_MAX);
349 break;
350 default:
351 usage();
352 } ARGEND;
353
354 if (argc != 1)
355 usage();
356
357 if (!vertical && !horizontal)
358 vertical = horizontal = 1;
359
360 eopen_stream(&colour, NULL);
361 eopen_stream(&sigma, argv[0]);
362
363 SELECT_PROCESS_FUNCTION(&colour);
364 CHECK_CHANS(&colour, == 3, == (measure_y_only ? 1 : colour.luma_…
365 CHECK_N_CHAN(&colour, 4, 4);
366
367 echeck_compat(&colour, &sigma);
368
369 if (jobs > colour.height)
370 jobs = colour.height;
371
372 if (glow)
373 original = emalloc(colour.frame_size);
374
375 fprint_stream_head(stdout, &colour);
376 efflush(stdout, "<stdout>");
377 process_each_frame_two_streams(&colour, &sigma, STDOUT_FILENO, "…
378 free(original);
379 return 0;
380 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.