1 /*
2  * GStreamer
3  * Copyright (C) 2008 Filippo Argiolas <filippo.argiolas@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <gst/gl/gstglconfig.h>
26 
27 #include "../gstgleffects.h"
28 #include "gstgleffectssources.h"
29 #include <math.h>
30 
31 /* A common file for sources is needed since shader sources can be
32  * generic and reused by several effects */
33 
34 /* FIXME */
35 /* Move sooner or later into single .frag .vert files and either bake
36  * them into a c file at compile time or load them at run time */
37 
38 
39 /* fill a normalized and zero centered gaussian vector for separable
40  * gaussian convolution */
41 
42 void
fill_gaussian_kernel(float * kernel,int size,float sigma)43 fill_gaussian_kernel (float *kernel, int size, float sigma)
44 {
45   int i;
46   float sum;
47   int l;
48 
49   /* need an odd sized vector to center it at zero */
50   g_return_if_fail ((size % 2) != 0);
51 
52   sum = 0.0;
53   l = (size - 1) / 2;
54 
55   for (i = 0; i < size; i++) {
56     kernel[i] = expf (-0.5 * pow ((i - l) / sigma, 2.0));
57     sum += kernel[i];
58   }
59 
60   for (i = 0; i < size; i++) {
61     kernel[i] /= sum;
62   }
63 }
64 
65 /* *INDENT-OFF* */
66 
67 /* Mirror effect */
68 const gchar *mirror_fragment_source_opengl =
69   "uniform sampler2D tex;"
70   "void main () {"
71   "  vec2 texturecoord = gl_TexCoord[0].xy;"
72   "  vec2 normcoord;"
73   "  normcoord = texturecoord - 0.5;"
74   "  normcoord.x *= sign (normcoord.x);"
75   "  texturecoord = normcoord + 0.5;"
76   "  vec4 color = texture2D (tex, texturecoord);"
77   "  gl_FragColor = color * gl_Color;"
78   "}";
79 
80 const gchar *mirror_fragment_source_gles2 =
81   "varying vec2 v_texcoord;"
82   "uniform sampler2D tex;"
83   "void main () {"
84   "  vec2 texturecoord = v_texcoord.xy;"
85   "  float normcoord = texturecoord.x - 0.5;"
86   "  normcoord *= sign (normcoord);"
87   "  texturecoord.x = normcoord + 0.5;"
88   "  gl_FragColor = texture2D (tex, texturecoord);"
89   "}";
90 
91 const gchar *squeeze_fragment_source_gles2 =
92   "varying vec2 v_texcoord;"
93   "uniform sampler2D tex;"
94   "void main () {"
95   "  vec2 texturecoord = v_texcoord.xy;"
96   "  vec2 normcoord = texturecoord - 0.5;"
97   /* Add a very small value to length otherwise it could be 0 */
98   "  float r = length (normcoord)+0.01;"
99   "  r = pow(r, 0.40)*1.3;"
100   "  normcoord = normcoord / r;"
101   "  texturecoord = (normcoord + 0.5);"
102   "  gl_FragColor = texture2D (tex, texturecoord);"
103   "}";
104 
105 const gchar *stretch_fragment_source_gles2 =
106   "varying vec2 v_texcoord;"
107   "uniform sampler2D tex;"
108   "void main () {"
109 	"  vec2 texturecoord = v_texcoord.xy;"
110   "  vec2 normcoord;"
111   "  normcoord = texturecoord - 0.5;"
112   "  float r = length (normcoord);"
113   "  normcoord *= 2.0 - smoothstep(0.0, 0.35, r);"
114   "  texturecoord = normcoord + 0.5;"
115   "  gl_FragColor = texture2D (tex, texturecoord);"
116   "}";
117 
118 const gchar *tunnel_fragment_source_gles2 =
119   "varying vec2 v_texcoord;"
120   "uniform sampler2D tex;"
121   "void main () {"
122   "  vec2 texturecoord = v_texcoord.xy;"
123   "  vec2 normcoord;"
124   /* little trick with normalized coords to obtain a circle with
125    * rect textures */
126   "  normcoord = (texturecoord - 0.5);"
127   "  float r = length(normcoord);"
128   "  if (r > 0.0)"
129   "    normcoord *= clamp (r, 0.0, 0.275) / r;"
130   "  texturecoord = normcoord + 0.5;"
131   "  gl_FragColor = texture2D (tex, texturecoord);"
132   "}";
133 
134 const gchar *fisheye_fragment_source_gles2 =
135   "varying vec2 v_texcoord;"
136   "uniform sampler2D tex;"
137   "void main () {"
138   "  vec2 texturecoord = v_texcoord.xy;"
139   "  vec2 normcoord;"
140   "  normcoord = texturecoord - 0.5;"
141   "  float r = length (normcoord);"
142   "  normcoord *= r * 1.41421;" /* sqrt (2) */
143   "  texturecoord = normcoord + 0.5;"
144   "  gl_FragColor = texture2D (tex, texturecoord);"
145   "}";
146 
147 const gchar *twirl_fragment_source_gles2 =
148   "varying vec2 v_texcoord;"
149   "uniform sampler2D tex;"
150   "void main () {"
151   "  vec2 texturecoord = v_texcoord.xy;"
152   "  vec2 normcoord;"
153   "  normcoord = texturecoord - 0.5;"
154   "  float r = length (normcoord);"
155   /* calculate rotation angle: maximum (about pi/2) at the origin and
156    * gradually decrease it up to 0.6 of each quadrant */
157   "  float phi = (1.0 - smoothstep (0.0, 0.3, r)) * 1.6;"
158   /* precalculate sin phi and cos phi, save some alu */
159   "  float s = sin(phi);"
160   "  float c = cos(phi);"
161   /* rotate */
162   "  normcoord *= mat2(c, s, -s, c);"
163   "  texturecoord = normcoord + 0.5;"
164   "  gl_FragColor = texture2D (tex, texturecoord);"
165   "}";
166 
167 const gchar *bulge_fragment_source_gles2 =
168   "varying vec2 v_texcoord;"
169   "uniform sampler2D tex;"
170   "void main () {"
171   "  vec2 texturecoord = v_texcoord.xy;"
172   "  vec2 normcoord;"
173   "  normcoord = texturecoord - 0.5;"
174   "  float r =  length (normcoord);"
175   "  normcoord *= smoothstep (-0.05, 0.25, r);"
176   "  texturecoord = normcoord + 0.5;"
177   "  gl_FragColor = texture2D (tex, texturecoord);"
178   "}";
179 
180 const gchar *square_fragment_source_gles2 =
181   "varying vec2 v_texcoord;"
182   "uniform sampler2D tex;"
183   "void main () {"
184   "  vec2 texturecoord = v_texcoord.xy;"
185   "  vec2 normcoord;"
186   "  normcoord = texturecoord - 0.5;"
187   "  float r = length (normcoord);"
188   "  normcoord *= 1.0 + smoothstep(0.125, 0.25, abs(normcoord));"
189   "  normcoord /= 2.0; /* zoom amount */"
190   "  texturecoord = normcoord + 0.5;"
191   "  gl_FragColor = texture2D (tex, texturecoord);"
192   "}";
193 
194 const gchar *luma_threshold_fragment_source_gles2 =
195   "varying vec2 v_texcoord;"
196   "uniform sampler2D tex;"
197   "void main () {"
198   "  vec2 texturecoord = v_texcoord.xy;"
199   "  vec4 color = texture2D(tex, texturecoord);"
200   "  float luma = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));"    /* BT.709 (from orange book) */
201   "  gl_FragColor = vec4 (vec3 (smoothstep (0.30, 0.50, luma)), color.a);"
202   "}";
203 
204 const gchar *sep_sobel_length_fragment_source_gles2 =
205   "varying vec2 v_texcoord;"
206   "uniform sampler2D tex;"
207   "uniform bool invert;"
208   "void main () {"
209   "  vec4 g = texture2D (tex, v_texcoord.xy);"
210   /* restore black background with grey edges */
211   "  g -= vec4(0.5, 0.5, 0.0, 0.0);"
212   "  float len = length (g);"
213   /* little trick to avoid IF operator */
214   /* TODO: test if a standalone inverting pass is worth */
215   "  gl_FragColor = abs(vec4(vec3(float(invert) - len), 1.0));"
216   "}";
217 
218 const gchar *desaturate_fragment_source_gles2 =
219   "varying vec2 v_texcoord;"
220   "uniform sampler2D tex;"
221   "void main () {"
222   "  vec4 color = texture2D (tex, v_texcoord.xy);"
223   "  float luma = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));"
224   "  gl_FragColor = vec4(vec3(luma), color.a);"
225   "}";
226 
227 const gchar *sep_sobel_hconv3_fragment_source_gles2 =
228   "varying vec2 v_texcoord;"
229   "uniform sampler2D tex;"
230   "uniform float width;"
231   "void main () {"
232   "  float w = 1.0 / width;"
233   "  vec2 texturecoord[3];"
234   "  texturecoord[1] = v_texcoord.xy;"
235   "  texturecoord[0] = texturecoord[1] - vec2(w, 0.0);"
236   "  texturecoord[2] = texturecoord[1] + vec2(w, 0.0);"
237   "  float grad_kern[3];"
238   "  grad_kern[0] = 1.0;"
239   "  grad_kern[1] = 0.0;"
240   "  grad_kern[2] = -1.0;"
241   "  float blur_kern[3];"
242   "  blur_kern[0] = 0.25;"
243   "  blur_kern[1] = 0.5;"
244   "  blur_kern[2] = 0.25;"
245   "  int i;"
246   "  vec4 sum = vec4 (0.0);"
247   "  for (i = 0; i < 3; i++) { "
248   "    vec4 neighbor = texture2D(tex, texturecoord[i]); "
249   "    sum.r = neighbor.r * blur_kern[i] + sum.r;"
250   "    sum.g = neighbor.g * grad_kern[i] + sum.g;"
251   "  }"
252   "  gl_FragColor = sum + vec4(0.0, 0.5, 0.0, 0.0);"
253   "}";
254 
255 const gchar *sep_sobel_vconv3_fragment_source_gles2 =
256   "varying vec2 v_texcoord;"
257   "uniform sampler2D tex;"
258   "uniform float height;"
259   "void main () {"
260   "  float h = 1.0 / height;"
261   "  vec2 texturecoord[3];"
262   "  texturecoord[1] = v_texcoord.xy;"
263   "  texturecoord[0] = texturecoord[1] - vec2(0.0, h);"
264   "  texturecoord[2] = texturecoord[1] + vec2(0.0, h);"
265   "  float grad_kern[3];"
266   "  grad_kern[0] = 1.0;"
267   "  grad_kern[1] = 0.0;"
268   "  grad_kern[2] = -1.0;"
269   "  float blur_kern[3];"
270   "  blur_kern[0] = 0.25;"
271   "  blur_kern[1] = 0.5;"
272   "  blur_kern[2] = 0.25;"
273   "  int i;"
274   "  vec4 sum = vec4 (0.0);"
275   "  for (i = 0; i < 3; i++) { "
276   "    vec4 neighbor = texture2D(tex, texturecoord[i]); "
277   "    sum.r = neighbor.r * grad_kern[i] + sum.r;"
278   "    sum.g = neighbor.g * blur_kern[i] + sum.g;"
279   "  }"
280   "  gl_FragColor = sum + vec4(0.5, 0.0, 0.0, 0.0);"
281   "}";
282 
283 const gchar *hconv7_fragment_source_gles2 =
284   "varying vec2 v_texcoord;"
285   "uniform sampler2D tex;"
286   "uniform float kernel[7];"
287   "uniform float gauss_width;"
288   "void main () {"
289   "  float w = 1.0 / gauss_width;"
290   "  vec2 texturecoord[7];"
291   "  texturecoord[3] = v_texcoord.xy;"
292   "  texturecoord[2] = texturecoord[3] - vec2(w, 0.0);"
293   "  texturecoord[1] = texturecoord[2] - vec2(w, 0.0);"
294   "  texturecoord[0] = texturecoord[1] - vec2(w, 0.0);"
295   "  texturecoord[4] = texturecoord[3] + vec2(w, 0.0);"
296   "  texturecoord[5] = texturecoord[4] + vec2(w, 0.0);"
297   "  texturecoord[6] = texturecoord[5] + vec2(w, 0.0);"
298   "  int i;"
299   "  vec4 sum = vec4 (0.0);"
300   "  for (i = 0; i < 7; i++) { "
301   "    vec4 neighbor = texture2D(tex, texturecoord[i]); "
302   "    sum += neighbor * kernel[i];"
303   "  }"
304   "  gl_FragColor = sum;"
305   "}";
306 
307 /* vertical convolution 7x7 */
308 const gchar *vconv7_fragment_source_gles2 =
309   "varying vec2 v_texcoord;"
310   "uniform sampler2D tex;"
311   "uniform float kernel[7];"
312   "uniform float gauss_height;"
313   "void main () {"
314   "  float h = 1.0 / gauss_height;"
315   "  vec2 texturecoord[7];"
316   "  texturecoord[3] = v_texcoord.xy;"
317   "  texturecoord[2] = texturecoord[3] - vec2(0.0, h);"
318   "  texturecoord[1] = texturecoord[2] - vec2(0.0, h);"
319   "  texturecoord[0] = texturecoord[1] - vec2(0.0, h);"
320   "  texturecoord[4] = texturecoord[3] + vec2(0.0, h);"
321   "  texturecoord[5] = texturecoord[4] + vec2(0.0, h);"
322   "  texturecoord[6] = texturecoord[5] + vec2(0.0, h);"
323   "  int i;"
324   "  vec4 sum = vec4 (0.0);"
325   "  for (i = 0; i < 7; i++) { "
326   "    vec4 neighbor = texture2D(tex, texturecoord[i]);"
327   "    sum += neighbor * kernel[i];"
328   "  }"
329   "  gl_FragColor = sum;"
330   "}";
331 
332 /* TODO: support several blend modes */
333 const gchar *sum_fragment_source_gles2 =
334   "varying vec2 v_texcoord;"
335   "uniform sampler2D base;"
336   "uniform sampler2D blend;"
337   "uniform float alpha;"
338   "uniform float beta;"
339   "void main () {"
340   "  vec4 basecolor = texture2D (base, v_texcoord.xy);"
341   "  vec4 blendcolor = texture2D (blend, v_texcoord.xy);"
342   "  gl_FragColor = alpha * basecolor + beta * blendcolor;"
343   "}";
344 
345 const gchar *multiply_fragment_source_gles2 =
346   "varying vec2 v_texcoord;"
347   "uniform sampler2D base;"
348   "uniform sampler2D blend;"
349   "uniform float alpha;"
350   "void main () {"
351   "  vec4 basecolor = texture2D (base, v_texcoord.xy);"
352   "  vec4 blendcolor = texture2D (blend, v_texcoord.xy);"
353   "  gl_FragColor = (1.0 - alpha) * basecolor + alpha * basecolor * blendcolor;"
354   "}";
355 
356 /* lut operations, map luma to tex1d, see orange book (chapter 19) */
357 const gchar *luma_to_curve_fragment_source_gles2 =
358   "varying vec2 v_texcoord;"
359   "uniform sampler2D tex;"
360   "uniform sampler2D curve;"
361   "void main () {"
362   "  vec2 texturecoord = v_texcoord.xy;"
363   "  vec4 color = texture2D (tex, texturecoord);"
364   "  float luma = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));"
365   "  color = texture2D (curve, vec2(luma, 0.0));"
366   "  gl_FragColor = color;"
367   "}";
368 
369 /* lut operations, map rgb to tex1d, see orange book (chapter 19) */
370 const gchar *rgb_to_curve_fragment_source_gles2 =
371   "varying vec2 v_texcoord;"
372   "uniform sampler2D tex;"
373   "uniform sampler2D curve;"
374   "void main () {"
375   "  vec4 color = texture2D (tex, v_texcoord.xy);"
376   "  vec4 outcolor;"
377   "  outcolor.r = texture2D (curve, vec2(color.r, 0.0)).r;"
378   "  outcolor.g = texture2D (curve, vec2(color.g, 0.0)).g;"
379   "  outcolor.b = texture2D (curve, vec2(color.b, 0.0)).b;"
380   "  outcolor.a = color.a;"
381   "  gl_FragColor = outcolor;"
382   "}";
383 
384 const gchar *sin_fragment_source_gles2 =
385   "varying vec2 v_texcoord;"
386   "uniform sampler2D tex;"
387   "void main () {"
388   "  vec4 color = texture2D (tex, vec2(v_texcoord.xy));"
389   "  float luma = dot(color.rgb, vec3(0.2125, 0.7154, 0.0721));"
390 /* calculate hue with the Preucil formula */
391   "  float cosh = color.r - 0.5*(color.g + color.b);"
392 /* sqrt(3)/2 = 0.866 */
393   "  float sinh = 0.866*(color.g - color.b);"
394 /* hue = atan2 h */
395   "  float sch = (1.0-sinh)*cosh;"
396 /* ok this is a little trick I came up because I didn't find any
397  * detailed proof of the Preucil formula. The issue is that tan(h) is
398  * pi-periodic so the smoothstep thing gives both reds (h = 0) and
399  * cyans (h = 180). I don't want to use atan since it requires
400  * branching and doesn't work on i915. So take only the right half of
401  * the circle where cosine is positive */
402 /* take a slightly purple color trying to get rid of human skin reds */
403 /* tanh = +-1.0 for h = +-45, where yellow=60, magenta=-60 */
404   "  float a = smoothstep (0.3, 1.0, sch);"
405   "  float b = smoothstep (-0.4, -0.1, sinh);"
406   "  float mix = a * b;"
407   "  gl_FragColor = color * mix + luma * (1.0 - mix);"
408   "}";
409 
410 const gchar *interpolate_fragment_source =
411   "varying vec2 v_texcoord;"
412   "uniform sampler2D base;"
413   "uniform sampler2D blend;"
414   "void main () {"
415   "vec4 basecolor = texture2D (base, v_texcoord);"
416   "vec4 blendcolor = texture2D (blend, v_texcoord);"
417   "vec4 white = vec4(1.0);"
418   "gl_FragColor = blendcolor + (1.0 - blendcolor.a) * basecolor;"
419   "}";
420 
421 const gchar *texture_interp_fragment_source =
422   "varying vec2 v_texcoord;"
423   "uniform sampler2D base;"
424   "uniform sampler2D blend;"
425   "uniform sampler2D alpha;"
426   "void main () {"
427   "  vec4 basecolor = texture2D (base, v_texcoord);"
428   "  vec4 blendcolor = texture2D (blend, v_texcoord);"
429   "  vec4 alphacolor = texture2D (alpha, v_texcoord);"
430   "  gl_FragColor = (alphacolor * blendcolor) + (1.0 - alphacolor) * basecolor;"
431   "}";
432 
433 const gchar *difference_fragment_source =
434   "varying vec2 v_texcoord;"
435   "uniform sampler2D saved;"
436   "uniform sampler2D current;"
437   "void main () {"
438   "vec4 savedcolor = texture2D (saved, v_texcoord);"
439   "vec4 currentcolor = texture2D (current, v_texcoord);"
440   "gl_FragColor = vec4 (step (0.12, length (savedcolor - currentcolor)));"
441   "}";
442 
443 /* This filter is meant as a demo of gst-plugins-gl + glsl
444    capabilities. So I'm keeping this shader readable enough. If and
445    when this shader will be used in production be careful to hard code
446    kernel into the shader and remove unneeded zero multiplications in
447    the convolution */
448 const gchar *conv9_fragment_source_gles2 =
449   "varying vec2 v_texcoord;"
450   "uniform sampler2D tex;"
451   "uniform float kernel[9];"
452   "uniform float width, height;"
453   "uniform bool invert;"
454   "void main () {"
455   "  float w = 1.0 / width;"
456   "  float h = 1.0 / height;"
457   "  vec2 texturecoord[9];"
458   "  texturecoord[4] = v_texcoord.xy;"                    /*  0  0 */
459   "  texturecoord[5] = texturecoord[4] + vec2(w,   0.0);" /*  1  0 */
460   "  texturecoord[2] = texturecoord[5] - vec2(0.0, h);"   /*  1 -1 */
461   "  texturecoord[1] = texturecoord[2] - vec2(w,   0.0);" /*  0 -1 */
462   "  texturecoord[0] = texturecoord[1] - vec2(w,   0.0);" /* -1 -1 */
463   "  texturecoord[3] = texturecoord[0] + vec2(0.0, h);"   /* -1  0 */
464   "  texturecoord[6] = texturecoord[3] + vec2(0.0, h);"   /* -1  1 */
465   "  texturecoord[7] = texturecoord[6] + vec2(w,   0.0);" /*  0  1 */
466   "  texturecoord[8] = texturecoord[7] + vec2(w,   0.0);" /*  1  1 */
467   "  int i;"
468   "  vec3 sum = vec3 (0.0);"
469   "  for (i = 0; i < 9; i++) { "
470   "    vec4 neighbor = texture2D (tex, texturecoord[i]);"
471   "    sum += neighbor.xyz * kernel[i];"
472   "  }"
473   "  gl_FragColor = vec4 (abs(sum - vec3(float(invert))), 1.0);"
474   "}";
475 
476 /* *INDENT-ON* */
477