///|
/// Build a tiled pyramid using nearest samples on the base image's pixel grid.
/// Every stored sample keeps its exact bits, including UINT IDs and float payloads.
/// For filtered color pyramids, supply your own levels to encode_tiled.
pub fn Image::nearest_levels(
  self : Image,
  tiles : TileDescription,
  limits~ : Limits = Limits::default(),
) -> Array[Image] raise ExrError {
  limits.validate()
  let levels = tiles.levels(self.data_window)
  let stride = channel_stride(self.channels())
  let mut bytes = 0
  let mut chunks = 0
  for level in levels {
    let pixels = level.window.pixels()
    if pixels > limits.max_pixels {
      raise Limit("pyramid level pixels")
    }
    bytes = add(bytes, mul(pixels, stride))
    chunks = add(chunks, mul(level.columns, level.rows))
    if bytes > limits.max_decoded_bytes || chunks > limits.max_chunks {
      raise Limit("pyramid decoded bytes or chunk count")
    }
  }
  levels.map(level => {
    if level.x == 0 && level.y == 0 {
      return self
    }
    let w = level.window
    let planes = self.planes.map(p => {
      let width = p.channel.kind.bytes()
      let source_width = self.data_window.width()
      let raw = sink()
      for y = 0; y < w.height(); y = y + 1 {
        for x = 0; x < w.width(); x = x + 1 {
          let sample = (y << level.y) * source_width + (x << level.x)
          let begin = sample * width
          for b in p.data[begin:begin + width] {
            raw.bytes.push(b)
          }
        }
      }
      Plane::from_bits(p.channel, w, raw.finish(), limits~)
    })
    Image::new(
      w,
      planes,
      display=self.display_window,
      metadata=self.metadata,
      pixel_aspect=self.pixel_aspect,
      screen_center=(self.screen_x, self.screen_y),
      screen_width=self.screen_width,
      limits~,
    )
  })
}