///|
/// A layer is the name prefix before the last dot, excluding the final component.
pub fn Channel::layer(self : Channel) -> String {
let bytes = @utf8.encode(self.name)
let mut last = -1
for i = 0; i < bytes.length(); i = i + 1 {
if bytes[i] == b'.' {
last = i
}
}
if last < 0 {
""
} else {
@utf8.decode_lossy(bytes[0:last])
}
}
///|
pub fn Image::layers(self : Image) -> Array[String] {
let seen : Map[String, Bool] = Map([])
let result : Array[String] = []
for ch in self.channels() {
let layer = ch.layer()
if !seen.contains(layer) {
seen[layer] = true
result.push(layer)
}
}
result.sort_by(name_compare)
result
}
///|
pub fn Image::select_layer(
self : Image,
layer : String,
limits~ : Limits = Limits::default(),
) -> Image raise ExrError {
let names = self
.channels()
.filter(ch => ch.layer() == layer)
.map(ch => ch.name)
self.select(names, limits~)
}
///|
/// Merge distinct channels only when all geometry, screen settings and metadata agree.
pub fn merge(
images : Array[Image],
limits~ : Limits = Limits::default(),
) -> Image raise ExrError {
if images.is_empty() {
raise Invalid("no images to merge", 0)
}
let first = images[0]
let planes : Array[Plane] = []
for img in images {
if img.data_window != first.data_window ||
img.display_window != first.display_window ||
img.pixel_aspect != first.pixel_aspect ||
img.screen() != first.screen() ||
img.metadata != first.metadata {
raise Invalid("merge requires equal windows screen and metadata", 0)
}
for p in img.planes {
planes.push(p)
}
}
first.with_planes(first.data_window, planes, limits)
}
///|
pub fn Image::with_metadata(
self : Image,
attrs : Array[Attribute],
limits~ : Limits = Limits::default(),
) -> Image raise ExrError {
Image::new(
self.data_window,
self.planes,
display=self.display_window,
metadata=attrs,
pixel_aspect=self.pixel_aspect,
screen_center=(self.screen_x, self.screen_y),
screen_width=self.screen_width,
limits~,
)
}
///|
test "layers use the last dot and merge refuses ambiguous geometry or names" {
let w = Window::new(0, 0, 0, 0)
let make = fn(name : String) -> Image raise ExrError {
Image::new(w, [
Plane::from_values({ name, kind: F32, linear: false, }, w, [1.0]),
])
}
let combined = merge([make("R"), make("view.left.R"), make("view.left.G")])
assert_eq(combined.layers(), ["", "view.left"])
assert_eq(combined.select_layer("view.left").channels().length(), 2)
assert_true(
try {
ignore(merge([make("R"), make("R")]))
false
} catch {
_ => true
},
)
assert_true(
try {
ignore(merge([make("R"), make("G").translate(1, 0)]))
false
} catch {
_ => true
},
)
assert_eq(
combined.with_metadata([Attribute::text("comments", "new")]).metadata()[0].as_text(),
"new",
)
}