///|
/// Paste absolute-coordinate channel samples without converting their bits.
/// The patch must fit completely; only matching channels are replaced.
pub fn Image::paste_region(
self : Image,
patch : Image,
limits~ : Limits = Limits::default(),
) -> Image raise ExrError {
ignore(self.with_planes(self.data_window, self.planes, limits))
let w = patch.data_window
if !self.data_window.contains(w.x0, w.y0) ||
!self.data_window.contains(w.x1, w.y1) {
raise Invalid("patch outside destination data window", 0)
}
for p in patch.planes {
if self.plane(p.channel.name).channel != p.channel {
raise Invalid("patch channel type or linearity differs", 0)
}
}
let planes : Array[Plane] = []
for base in self.planes {
let mut replacement : Plane? = None
for p in patch.planes {
if p.channel.name == base.channel.name {
replacement = Some(p)
}
}
match replacement {
None => planes.push(base)
Some(p) => {
let data = Array::makei(base.data.length(), i => base.data[i])
let stride = base.channel.kind.bytes()
let row_bytes = mul(w.width(), stride)
for row = 0; row < w.height(); row = row + 1 {
let start = mul(self.data_window.offset(w.x0, w.y0 + row), stride)
for b = 0; b < row_bytes; b = b + 1 {
data[start + b] = p.data[row * row_bytes + b]
}
}
planes.push(
Plane::from_bits(
base.channel,
base.window,
Bytes::from_array(data),
limits~,
),
)
}
}
}
self.with_planes(self.data_window, planes, limits)
}
///|
pub(all) struct Quantiles {
finite_samples : Int32
nonfinite_samples : Int32
values : Array[Double?]
}
///|
/// Finite-only nearest-rank empirical quantiles, in caller probability order.
/// p=0 selects the minimum. Empty finite populations produce None.
pub fn Plane::quantiles(
self : Plane,
probabilities : Array[Double],
limits~ : Limits = Limits::default(),
) -> Quantiles raise ExrError {
limits.validate()
let count = self.window.pixels()
if count > limits.max_pixels ||
probabilities.length() > limits.max_attributes ||
mul(add(count, probabilities.length()), 8) > limits.max_decoded_bytes {
raise Limit("quantile working set")
}
for p in probabilities {
if !finite(p) || p < 0.0 || p > 1.0 {
raise Invalid("quantile probability must be finite in 0..1", 0)
}
}
let values : Array[Double] = []
let r = reader(self.data)
while r.pos < r.end {
let bits = r.uint(self.channel.kind.bytes()).to_uint()
let v = match self.channel.kind {
U32 => bits.to_double()
F16 => half_value(bits.reinterpret_as_int())
F32 => Float::reinterpret_from_uint(bits).to_double()
}
if finite(v) {
values.push(v)
}
}
values.sort_by((a, b) => if a < b { -1 } else if a > b { 1 } else { 0 })
let output : Array[Double?] = []
for p in probabilities {
if values.is_empty() {
output.push(None)
} else {
let rank = p * values.length().to_double()
let floor = rank.to_int()
let ceiling = if rank > floor.to_double() { floor + 1 } else { floor }
output.push(Some(values[imax(0, imin(values.length() - 1, ceiling - 1))]))
}
}
{
finite_samples: values.length(),
nonfinite_samples: count - values.length(),
values: output,
}
}