#version 420 // Motion blur — direct port of Android `EditorImageMotionBlurFilter`. // This is what the Android editor actually uses for the // "Horizontal" / "Vertical" Blur Style choices: 40 jittered samples // along a 1D direction, parabolic weights `4*(p - p*p)`, no Gaussian // falloff. The dense sample count keeps the visual smooth at high // strengths (no aliasing bands like the 9-tap `direction_v1` port // produced), and the parabolic weight curve gives the soft long- // exposure look characteristic of Android's high-strength preset. // // Direction mapping (mirrors Android's CPU-side setup): // `direction.xy = blurSize * (dirX, dirY) * 4 / (srcW, srcH)` (UV) // The per-sample movement is `direction * (percent - 0.5)` for // `percent in [0, 1]`, so the kernel reach in source pixels is // `[-blurSize * 2, +blurSize * 2]` along the chosen axis. // // `blurRandom` adds a per-pixel sub-sample jitter so the 40 samples // don't align across neighbouring pixels — this is what keeps the // output looking smooth instead of striped at very large `blurSize`. layout(location = 0) in vec2 textureCoordinate; layout(location = 0) out vec4 fragColor; layout(std140, binding = 0) uniform buf { mat4 qt_Matrix; float qt_Opacity; float blurSize; float dirX; float dirY; float srcW; float srcH; }; layout(binding = 1) uniform sampler2D source; float blurRandom(vec2 co) { return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453); } void main() { vec4 centreColor = texture(source, textureCoordinate); if (blurSize <= 0.0 || srcW <= 0.0 || srcH <= 0.0) { fragColor = centreColor * qt_Opacity; return; } vec2 direction = vec2(blurSize * dirX * 4.0 / srcW, blurSize * dirY * 4.0 / srcH); float jitter = blurRandom(textureCoordinate); vec4 sumColor = vec4(0.0); float sumWeight = 0.0; const int kSampleNum = 40; const float kSampleNumF = 40.0; for (int t = 0; t <= kSampleNum; ++t) { float percent = (float(t) + jitter) / kSampleNumF; float weight = 4.0 * (percent - percent * percent); vec2 movement = direction * (percent - 0.5); sumColor += texture(source, textureCoordinate + movement) * weight; sumWeight += weight; } fragColor = (sumColor / sumWeight) * qt_Opacity; }