{
  "name": "Sparkle",
  "type": "static",
  "passes": [
    {
      "name": "sparkle",
      "inputs": [
        "default"
      ],
      "glsl": "varying vec2 sourceTextureCoordinate;

uniform vec2 outputSize;

uniform float lowThreshold;
uniform float highThreshold;
uniform float radius;
uniform float r;
uniform float g;
uniform float b;

#define GRID_X 18
#define GRID_Y 24
#define SOFT 0.2

void main() {

  vec2 uv = sourceTextureCoordinate;
  vec4 base = sampleInput(uv);

  vec2 asp = vec2(outputSize.x / outputSize.y, 1.0) * 4.0;
  float ang = 0.78539816339;
  float ca = cos(ang);
  float sa = sin(ang);
  mat2 rot2d = mat2(ca, -sa, sa, ca);

  vec3 sparkleAccum = vec3(0.0);

  for (int i = 0; i < GRID_X * GRID_Y; i++){

    float fi = float(i);
    float x = mod(fi, float(GRID_X));
    float y = floor(fi / float(GRID_X));
    float fx = (x + 0.5) / float(GRID_X);
    float fy = (y + 0.5) / float(GRID_Y);

    vec2 cellUV = vec2(fx, fy);

    float lum = dot(sampleInput(cellUV).rgb, vec3(0.299, 0.587, 0.114));

    float str = smoothstep(lowThreshold, highThreshold, lum) * (1.0 - step(highThreshold, lum));
    if (str <= 0.2) continue;

    vec2 p0 = (uv - cellUV) * asp;
    vec2 p = rot2d * p0;
    float falloff = sqrt(abs(p.x)) + sqrt(abs(p.y));
    float denom = max(10.0, radius * falloff + 2.0);
    float br = 250.0 * pow(1.0/denom, 2.5);

    vec3 star = mix(vec3(r * br, g * br, b * br), vec3(1.0), br) * str;
    sparkleAccum += star;
  }

  vec3 color = base.rgb + sparkleAccum;
  gl_FragColor = toOutputFormat(vec4(color,1.0));
}
",
      "metal": "using namespace metal;
struct fragmentIn { float2 textureCoordinate [[user(locn0)]]; float2 sourceTextureCoordinate [[user(locn1)]]; };
struct fragmentOut { float4 _gl_FragColor [[color(0)]]; };
typedef struct {
  float4x4 content_transform;
  float4x4 texture_transform;
  float strength;
  float progress;
  float time_sec;
  float duration_sec;
  float clip_duration_sec;
  float2 input_size;
  float2 output_size;
  float lowThreshold;
  float highThreshold;
  float radius;
  float r;
  float g;
  float b;
 } Uniforms;

    float mod(float x, float y) { return x - y * floor(x / y); }
    float2 mod(float2 x, float2 y) { return x - y * floor(x / y); }
    float3 mod(float3 x, float3 y) { return x - y * floor(x / y); }
    float4 mod(float4 x, float4 y) { return x - y * floor(x / y); }
#define GRID_X 18
#define GRID_Y 24
#define SOFT 0.2

fragment fragmentOut fragmentShader(fragmentIn in [[stage_in]],
 constant Uniforms & uniforms [[buffer(1)]],
 DefaultInputs defaultInputs) {

  float2 uv = in.sourceTextureCoordinate;
  float4 base = sampleInput(defaultInputs, uv);

  float2 asp = float2(uniforms.output_size.x / uniforms.output_size.y, 1.0) * 4.0;
  float ang = 0.78539816339;
  float ca = cos(ang);
  float sa = sin(ang);
  float2x2 rot2d = float2x2(ca, -sa, sa, ca);

  float3 sparkleAccum = float3(0.0);

  for (int i = 0; i < GRID_X * GRID_Y; i++){

    float fi = float(i);
    float x = mod(fi, float(GRID_X));
    float y = floor(fi / float(GRID_X));
    float fx = (x + 0.5) / float(GRID_X);
    float fy = (y + 0.5) / float(GRID_Y);

    float2 cellUV = float2(fx, fy);

    float lum = dot(sampleInput(defaultInputs, cellUV).rgb, float3(0.299, 0.587, 0.114));

    float str = smoothstep(uniforms.lowThreshold, uniforms.highThreshold, lum) * (1.0 - step(uniforms.highThreshold, lum));
    if (str <= 0.2) continue;

    float2 p0 = (uv - cellUV) * asp;
    float2 p = rot2d * p0;
    float falloff = sqrt(abs(p.x)) + sqrt(abs(p.y));
    float denom = max(10.0, uniforms.radius * falloff + 2.0);
    float br = 250.0 * pow(1.0/denom, 2.5);

    float3 star = mix(float3(uniforms.r * br, uniforms.g * br, uniforms.b * br), float3(1.0), br) * str;
    sparkleAccum += star;
  }

  float3 color = base.rgb + sparkleAccum;
  return {toOutputFormat(float4(color,1.0))};
}
",
      "uniforms": {
        "lowThreshold": {
          "value": 0.6
        },
        "highThreshold": {
          "value": 0.64
        },
        "radius": {
          "value": 80.0
        },
        "r": {
          "value": 0.5
        },
        "g": {
          "value": 0.0
        },
        "b": {
          "value": 0.5
        }
      }
    }
  ]
}
