Swirlomatic

The Swirlomatic is a component-wise kernel which does random access on its single input. It warps a circular area of the input image around in a swirl.

Change the Amount parameter to vary the tightness of the swirl. You can also change the Radius of the swirl and the position of its Centre.

// Copyright (c) 2024 The Foundry Visionmongers Ltd.  All Rights Reserved.

/// Swirlomatic kernel: Does a nice swirl. Amount is in degrees.
kernel Swirlomatic : ImageComputationKernel<eComponentWise> {
  // Input and output images
  Image<eRead, eAccessRandom, eEdgeClamped> src;  // randomly accessing and edge clamping
  Image<eWrite, eAccessPoint> dst;

// Parameters are made available to the user as knobs.
param:
  float amount;   // Swirl amount in degrees
  int size;       // Swirl radius
  float2 centre;  // Swirl centre

// Local variables can be initialised once in init() and used from all pixel positions.
local:
  float sizeInvSquared;

  // In define(), parameters can be given labels and default values.
  // Size and Centre are affected by the proxy scaling.
  void define() {
    defineParam(amount, "Amount", 180.0f);
    defineParam(size, "Radius", 320, eParamProxyScale);
    defineParam(centre, "Centre", float2(640.0f, 320.0f), eParamProxyScale);
  }

  // The init() function is run once before any calls to process().
  void init() {
    sizeInvSquared = size > 0 ? 1.0f / size : 0.f;
    sizeInvSquared *= sizeInvSquared;
  }

  // The process() function runs over all pixel positions of the output image.
  // The int3 pos parameter is used to indicate positions x, y and the component.
  void process(int3 pos) {
    // Calculate the distance from the swirl's centre.
    const float dx = float(pos.x) - centre.x;
    const float dy = float(pos.y) - centre.y;
    const float distanceSquared = dx * dx + dy * dy;

    // Calculate delta as quadratic distance from the centre
    // in order to rotate proportionally to the swirl amount.
    float delta = 1.0f - ((distanceSquared) * sizeInvSquared);
    if (delta < 0.f) delta = 0.f; // exclude points outside the radius

    // Calculate sine and cosine for the rotation.
    const float toRadians = PI / 180.0f;
    const float angle = delta * amount * toRadians;
    const float sine = sin(angle);
    const float cosine = cos(angle);

    // Find the sampling pixel positions for anti-clockwise rotation.
    const float x = centre.x + dx * cosine + dy * sine;
    const float y = centre.y - dx * sine + dy * cosine;

    // If src has less components than dst, protect from reading non-existent channels.
    const int component = pos.z;
    if (component < src.kComps) {
      // Apply bilinear interpolation to sample from the input.
      dst() = bilinear(src, x, y);
    }
  }
};