Pass image as argument to distortionEffect metal shader

I spent a majority of the evening trying to figure out how to pass an image into an MSL shader. Here’s what I got working:

#define TAU 6.28318530717958647692528676656

[[stitchable]] float2 myDistortion(
    float2 position,
    float2 resolution,
    texture2d<half> source [[ texture(0)]]
    ) {
    float2 uv = position / resolution;
    //^^ Normalize coordinates

    constexpr sampler texSampler (mag_filter::linear, min_filter::linear);
    //^^ Create texture sampler
    half4 color = source.sample(texSampler, uv);
    //^^ sample the texture at the uv coordinates
    //... transform coordinates by color
    float angle = avg_color(color) * TAU;
    position = (uv + float2( cos(angle), sin(angle))) * resolution;
    return position;
}

Then, in the visualEffect modifier, give the distortion effect the resolution and the image. The image is converted into a texture2d automatically.


struct MyView: View {
    var image: Image?
    var enableDistortion: Bool

    var body: some View {
        image?
            .resizable()
            .aspectRatio(contentMode: .fit)
            .visualEffect { content, proxy in
                content
                    .distortionEffect(
                        ShaderLibrary.myDistortion(.float2(proxy.size), .image(image!)),
                        maxSampleOffset: .zero,
                        isEnabled: enableDistortion
                        )
            }
    }

}