(function attachPyroBitmapRetouch(root) {
  'use strict';

  const finite = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
  const clamp = (value, minimum, maximum) => Math.min(maximum, Math.max(minimum, value));
  const smoothstep = (value) => { const t = clamp(finite(value), 0, 1); return t * t * (3 - 2 * t); };

  function pixel(source, width, height, xValue, yValue) {
    const x = clamp(Math.round(finite(xValue)), 0, width - 1); const y = clamp(Math.round(finite(yValue)), 0, height - 1);
    const offset = (y * width + x) * 4;
    return [source[offset], source[offset + 1], source[offset + 2], source[offset + 3]];
  }

  function median(values, fallback = 0) {
    if (!values.length) return fallback;
    values.sort((left, right) => left - right); const middle = Math.floor(values.length / 2);
    return values.length % 2 ? values[middle] : (values[middle - 1] + values[middle]) / 2;
  }

  function averageWindow(source, width, height, x, y, radiusValue, fallback) {
    const radius = Math.max(0, Math.round(finite(radiusValue))); const sums = [0, 0, 0, 0]; let weight = 0;
    for (let offsetY = -radius; offsetY <= radius; offsetY += 1) {
      for (let offsetX = -radius; offsetX <= radius; offsetX += 1) {
        const sample = pixel(source, width, height, x + offsetX, y + offsetY); const alpha = sample[3] / 255;
        if (alpha <= 0) continue;
        sums[0] += sample[0] * alpha; sums[1] += sample[1] * alpha; sums[2] += sample[2] * alpha; sums[3] += sample[3]; weight += alpha;
      }
    }
    return weight > 0 ? [sums[0] / weight, sums[1] / weight, sums[2] / weight, sums[3] / Math.max(1, (radius * 2 + 1) ** 2)] : [...fallback];
  }

  function spotHealPatch(sourcePixels, widthValue, heightValue, center, options = {}) {
    const width = Math.max(1, Math.round(finite(widthValue, 1))); const height = Math.max(1, Math.round(finite(heightValue, 1)));
    if (!sourcePixels || sourcePixels.length < width * height * 4) throw new Error('Bitmapdaten sind unvollständig');
    const radius = Math.max(.5, finite(options.radius, 9)); const softness = clamp(finite(options.softness, 45), 0, 100);
    const shape = options.shape === 'square' ? 'square' : 'circle'; const centerX = finite(center?.x) + .5; const centerY = finite(center?.y) + .5;
    const left = clamp(Math.floor(centerX - radius), 0, width - 1); const top = clamp(Math.floor(centerY - radius), 0, height - 1);
    const right = clamp(Math.ceil(centerX + radius), 0, width); const bottom = clamp(Math.ceil(centerY + radius), 0, height);
    const patchWidth = right - left; const patchHeight = bottom - top; if (patchWidth <= 0 || patchHeight <= 0) return null;

    const ring = [[], [], [], []]; const outer = Math.max(radius + 1, radius * 1.65); const inner = radius * 1.05;
    const ringLeft = Math.floor(centerX - outer); const ringTop = Math.floor(centerY - outer);
    const ringRight = Math.ceil(centerX + outer); const ringBottom = Math.ceil(centerY + outer);
    for (let y = ringTop; y <= ringBottom; y += 1) {
      for (let x = ringLeft; x <= ringRight; x += 1) {
        const distance = Math.hypot(x + .5 - centerX, y + .5 - centerY); if (distance < inner || distance > outer) continue;
        const sample = pixel(sourcePixels, width, height, x, y); if (!sample[3]) continue;
        for (let channel = 0; channel < 4; channel += 1) ring[channel].push(sample[channel]);
      }
    }
    const fallback = pixel(sourcePixels, width, height, centerX, centerY);
    const surround = ring.map((values, channel) => median(values, fallback[channel]));
    const windowRadius = Math.max(0, Math.round(radius * .12)); const sampleDistance = radius * 1.28;
    const leftColor = averageWindow(sourcePixels, width, height, centerX - sampleDistance, centerY, windowRadius, surround);
    const rightColor = averageWindow(sourcePixels, width, height, centerX + sampleDistance, centerY, windowRadius, surround);
    const topColor = averageWindow(sourcePixels, width, height, centerX, centerY - sampleDistance, windowRadius, surround);
    const bottomColor = averageWindow(sourcePixels, width, height, centerX, centerY + sampleDistance, windowRadius, surround);
    const data = new Uint8ClampedArray(patchWidth * patchHeight * 4); const hardRadius = radius * (1 - softness / 100);

    for (let y = 0; y < patchHeight; y += 1) {
      for (let x = 0; x < patchWidth; x += 1) {
        const absoluteX = left + x; const absoluteY = top + y; const dx = absoluteX + .5 - centerX; const dy = absoluteY + .5 - centerY;
        const distance = shape === 'square' ? Math.max(Math.abs(dx), Math.abs(dy)) : Math.hypot(dx, dy);
        const sourceOffset = (absoluteY * width + absoluteX) * 4; const targetOffset = (y * patchWidth + x) * 4;
        for (let channel = 0; channel < 4; channel += 1) data[targetOffset + channel] = sourcePixels[sourceOffset + channel];
        if (distance > radius) continue;
        const strength = distance <= hardRadius || softness <= 0 ? 1 : smoothstep((radius - distance) / Math.max(.0001, radius - hardRadius));
        const horizontalRatio = clamp((dx / radius + 1) / 2, 0, 1); const verticalRatio = clamp((dy / radius + 1) / 2, 0, 1);
        const angle = Math.atan2(dy, dx) + ((absoluteX * 17 + absoluteY * 29) % 11 - 5) * .035;
        const textureSample = pixel(sourcePixels, width, height, centerX + Math.cos(angle) * sampleDistance, centerY + Math.sin(angle) * sampleDistance);
        for (let channel = 0; channel < 4; channel += 1) {
          const horizontal = leftColor[channel] * (1 - horizontalRatio) + rightColor[channel] * horizontalRatio;
          const vertical = topColor[channel] * (1 - verticalRatio) + bottomColor[channel] * verticalRatio;
          const gradient = (horizontal + vertical) / 2; const texture = (textureSample[channel] - surround[channel]) * (channel === 3 ? .08 : .28);
          const replacement = clamp(gradient * .76 + surround[channel] * .24 + texture, 0, 255);
          data[targetOffset + channel] = Math.round(sourcePixels[sourceOffset + channel] * (1 - strength) + replacement * strength);
        }
      }
    }
    return { left, top, width: patchWidth, height: patchHeight, data };
  }

  const api = Object.freeze({ spotHealPatch });
  root.PyroBitmapRetouch = api;
  if (typeof module === 'object' && module?.exports) module.exports = api;
})(typeof globalThis !== 'undefined' ? globalThis : self);
