///|
pub(all) enum LevelMode {
OneLevel
Mipmap
Ripmap
} derive(Eq, Debug)
///|
pub(all) struct TileDescription {
width : Int32
height : Int32
mode : LevelMode
round_up : Bool
} derive(Eq, Debug)
///|
pub(all) struct LevelInfo {
x : Int32
y : Int32
window : Window
columns : Int32
rows : Int32
} derive(Eq, Debug)
///|
fn level_size(size : Int32, level : Int32, round_up : Bool) -> Int32 {
let unit = 1L << level
let bias = if round_up { unit - 1L } else { 0L }
imax(1, ((size.to_int64() + bias) / unit).to_int())
}
///|
fn level_count(size : Int32, round_up : Bool) -> Int32 {
let mut level = 0
while level_size(size, level, round_up) > 1 {
level += 1
}
level + 1
}
///|
/// Levels in offset-table order. The base data window's origin applies to all levels.
pub fn TileDescription::levels(
self : TileDescription,
base : Window,
) -> Array[LevelInfo] raise ExrError {
if self.width <= 0 || self.height <= 0 {
raise Invalid("invalid tile dimensions", 0)
}
let nx = level_count(base.width(), self.round_up)
let ny = level_count(base.height(), self.round_up)
let (columns, rows) = match self.mode {
OneLevel => (1, 1)
Mipmap => (1, imax(nx, ny))
Ripmap => (nx, ny)
}
let result = []
for y = 0; y < rows; y = y + 1 {
for i = 0; i < columns; i = i + 1 {
let x = if self.mode == Mipmap { y } else { i }
let w = level_size(base.width(), x, self.round_up)
let h = level_size(base.height(), y, self.round_up)
result.push({
x,
y,
window: Window::new(
base.x0,
base.y0,
base.x0 + (w - 1),
base.y0 + (h - 1),
),
columns: (w - 1) / self.width + 1,
rows: (h - 1) / self.height + 1,
})
}
}
result
}
///|
fn read_tiles(bytes : Bytes) -> TileDescription raise ExrError {
if bytes.length() != 9 {
raise Invalid("tiledesc size", 0)
}
let r = reader(bytes)
let width = index(r.uint(4))
let height = index(r.uint(4))
let flags = r.u8()
let mode = match flags & 15 {
0 => OneLevel
1 => Mipmap
2 => Ripmap
_ => raise Invalid("tile level mode", 0)
}
if width == 0 || height == 0 || flags >= 32 {
raise Invalid("tile dimensions or rounding mode", 0)
}
{ width, height, mode, round_up: flags >= 16, }
}
///|
fn TileDescription::attribute(self : TileDescription) -> Attribute {
let mode = match self.mode {
OneLevel => 0
Mipmap => 1
Ripmap => 2
}
let out = sink()
out.i32(self.width)
out.i32(self.height)
let flags = mode + (if self.round_up { 16 } else { 0 })
out.uint(flags.to_uint64(), 1)
{ name: "tiles", kind: "tiledesc", value: out.finish(), }
}
///|
fn block_levels(h : Header) -> Array[LevelInfo] raise ExrError {
let w = h.data_window
match h.tiles {
Some(t) => t.levels(w)
None =>
[
{
x: 0,
y: 0,
window: w,
columns: 1,
rows: (w.height() - 1) / h.compression.scanlines() + 1,
},
]
}
}
///|
fn block_size(h : Header) -> (Int32, Int32) {
match h.tiles {
Some(t) => (t.width, t.height)
None => (h.data_window.width(), h.compression.scanlines())
}
}
///|
fn block_window(
level : LevelInfo,
size : (Int32, Int32),
x : Int32,
y : Int32,
) -> Window {
let (width, height) = size
let w = level.window
let x0 = x * width
let y0 = y * height
{
x0: w.x0 + x0,
y0: w.y0 + y0,
x1: w.x0 + (x0 + imin(width, w.width() - x0) - 1),
y1: w.y0 + (y0 + imin(height, w.height() - y0) - 1),
}
}
///|
pub extend LevelMode with Eq::{equal, not_equal}
///|
pub extend LevelMode with @debug.Debug::{to_repr}
///|
pub extend TileDescription with Eq::{equal, not_equal}
///|
pub extend TileDescription with @debug.Debug::{to_repr}
///|
pub extend LevelInfo with Eq::{equal, not_equal}
///|
pub extend LevelInfo with @debug.Debug::{to_repr}