filters/convolution.js

/**
 * Generic Convolution Engine
 * Performance notes:
 * - Convolution traverses almost every pixel and applies kernel math per pixel.
 * - Repeated passes or large images can block the main thread.
 * - Prefer resizing before convolution when running live previews.
 *
 * @param {Uint8ClampedArray} data - RGBA pixel data
 * @param {number} width - Image width
 * @param {number} height - Image height
 * @param {number[]} kernel - A 3x3 matrix (array of 9 numbers)
 */
export const applyConvolution = (data, width, height, kernel) => {
  const buffer = new Uint8ClampedArray(data);

  // Iterate through every pixel except the 1-px border
  for (let y = 1; y < height - 1; y++) {
    for (let x = 1; x < width - 1; x++) {
      let r = 0,
        g = 0,
        b = 0;

      // Apply the 3x3 kernel matrix
      for (let ky = 0; ky < 3; ky++) {
        for (let kx = 0; kx < 3; kx++) {
          const nx = x + kx - 1;
          const ny = y + ky - 1;
          const offset = (ny * width + nx) * 4;
          const weight = kernel[ky * 3 + kx];

          r += buffer[offset] * weight;
          g += buffer[offset + 1] * weight;
          b += buffer[offset + 2] * weight;
        }
      }

      const i = (y * width + x) * 4;
      data[i] = Math.min(255, Math.max(0, r)); // Red
      data[i + 1] = Math.min(255, Math.max(0, g)); // Green
      data[i + 2] = Math.min(255, Math.max(0, b)); // Blue
    }
  }
  return data;
};