///|
/// Additional production filters built from the core integer raster API.
pub(all) enum ToneMap {
  Linear
  Filmic
  Reinhard
  HighlightRollOff
} derive(Eq, Debug)

///|
fn tone_byte(value : Int) -> Byte {
  (if value < 0 { 0 } else if value > 255 { 255 } else { value }).to_byte()
}

///|
/// Apply a tone map to RGB channels while preserving alpha.
pub fn Canvas::tone_map(
  self : Canvas,
  mode : ToneMap,
  exposure : Int,
) -> Canvas {
  let result = self.copy()
  let safe_exposure = if exposure < 0 {
    0
  } else if exposure > 4000 {
    4000
  } else {
    exposure
  }
  for i in 0.. Byte {
      let scaled = value.to_int() * (1000 + safe_exposure) / 1000
      let output = match mode {
        Linear => scaled
        Filmic => scaled * 1000 / (1000 + scaled)
        Reinhard => scaled * 1200 / (1200 + scaled)
        HighlightRollOff =>
          if scaled > 180 {
            180 + (scaled - 180) / 4
          } else {
            scaled
          }
      }
      tone_byte(output)
    }
    result.pixels[i] = rgba(
      map(source.red()),
      map(source.green()),
      map(source.blue()),
      source.alpha(),
    )
  }
  result
}

///|
/// Smooth a canvas using a separable three-tap kernel.
pub fn Canvas::smooth3(self : Canvas) -> Canvas {
  let horizontal = Canvas::new(
    width=self.width,
    height=self.height,
    fill=rgba(0, 0, 0, 0),
  )
  for y in 0..= self.width { x } else { x + 1 }
      let left = self.pixels[left_x + y * self.width]
      let center = self.pixels[x + y * self.width]
      let right = self.pixels[right_x + y * self.width]
      horizontal.pixels[x + y * self.width] = rgba(
        ((
          left.red().to_int() + 2 * center.red().to_int() + right.red().to_int()
        ) /
        4).to_byte(),
        ((
          left.green().to_int() +
          2 * center.green().to_int() +
          right.green().to_int()
        ) /
        4).to_byte(),
        ((
          left.blue().to_int() +
          2 * center.blue().to_int() +
          right.blue().to_int()
        ) /
        4).to_byte(),
        ((
          left.alpha().to_int() +
          2 * center.alpha().to_int() +
          right.alpha().to_int()
        ) /
        4).to_byte(),
      )
    }
  }
  let result = Canvas::new(
    width=self.width,
    height=self.height,
    fill=rgba(0, 0, 0, 0),
  )
  for y in 0..= self.height { y } else { y + 1 }
      let top = horizontal.pixels[x + top_y * self.width]
      let center = horizontal.pixels[x + y * self.width]
      let bottom = horizontal.pixels[x + bottom_y * self.width]
      result.pixels[x + y * self.width] = rgba(
        ((
          top.red().to_int() + 2 * center.red().to_int() + bottom.red().to_int()
        ) /
        4).to_byte(),
        ((
          top.green().to_int() +
          2 * center.green().to_int() +
          bottom.green().to_int()
        ) /
        4).to_byte(),
        ((
          top.blue().to_int() +
          2 * center.blue().to_int() +
          bottom.blue().to_int()
        ) /
        4).to_byte(),
        ((
          top.alpha().to_int() +
          2 * center.alpha().to_int() +
          bottom.alpha().to_int()
        ) /
        4).to_byte(),
      )
    }
  }
  result
}

///|
/// Unsharp-mask a canvas using the smooth3 pass.
pub fn Canvas::unsharp3(self : Canvas, amount : Int) -> Canvas {
  let safe_amount = if amount < 0 {
    0
  } else if amount > 1000 {
    1000
  } else {
    amount
  }
  let blurred = self.smooth3()
  let result = self.copy()
  for i in 0.. Canvas {
  let posterized = self.posterize(levels)
  if smooth {
    posterized.smooth3()
  } else {
    posterized
  }
}

///|
/// Detect high-contrast pixels into an alpha mask.
pub fn Canvas::contrast_mask(self : Canvas, threshold : Int) -> Canvas {
  let result = Canvas::new(
    width=self.width,
    height=self.height,
    fill=rgba(0, 0, 0, 0),
  )
  for y in 0..= threshold {
        rgba(255, 255, 255, distance.to_byte())
      } else {
        rgba(0, 0, 0, 0)
      }
    }
  }
  result
}