///|
/// 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~,
)
})
}