use super::thumbnail;
use crate::{
error::Error,
protocol::{
caps::{Capabilities, address::CoordinateBase, other::DataTypes},
data::{Boundary, Op, Rect},
},
};
use tracing::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Framing {
Published,
Thumbnail,
Perforation,
Address,
}
impl Framing {
pub fn choose(caps: &Capabilities) -> Self {
let rects = caps
.address
.coordinate_base
.contains(CoordinateBase::FRAME_RECTS);
if rects {
if caps.frames.as_ref().is_some_and(|f| f.measured()) {
return Self::Published;
}
if thumbnail::available(caps) {
return Self::Thumbnail;
}
return Self::Address;
}
if caps
.features
.data_types
.contains(DataTypes::PERFORATION_READ)
{
return Self::Perforation;
}
Self::Address
}
}
pub(crate) fn reachable(caps: &Capabilities, extent: u32) -> Result<(), Error> {
if extent == 0 {
return Err(Error::Unsupported {
op: "frame table",
reason: "a frame of length 0 is not a frame".into(),
});
}
let limit = caps.address.y_axis.boundary;
match extent > limit {
true => Err(Error::Unsupported {
op: "frame table",
reason: format!(
"a frame of {extent} is past the {limit} boundary and would stall the stage"
),
}),
false => Ok(()),
}
}
pub fn table(caps: &Capabilities) -> Result<Boundary, Error> {
let Some(published) = caps.frames.as_ref() else {
return Ok(Boundary::default());
};
let mut frames = Vec::new();
for image in &published.images {
let Some(extent) = image.length else {
continue;
};
let extent = super::window::whole_blocks(caps, extent);
reachable(caps, extent)?;
frames.push(Rect {
top: image.top,
left: image.left,
bottom: image.top + extent,
right: image.left + image.width,
});
}
Ok(Boundary { frames })
}
pub fn frames_on_medium(caps: &Capabilities) -> u32 {
let y = &caps.address.y_axis;
let span = y.address_range.last.saturating_sub(y.address_range.start) + 1;
match y.boundary {
0 => 1,
boundary => (span / boundary).max(1),
}
}
pub fn self_feeding(caps: &Capabilities) -> bool {
caps.features.execute.supports(Op::Load) && frames_on_medium(caps) == 1
}
pub fn frames(caps: &Capabilities) -> Result<Boundary, Error> {
let (x, y) = (&caps.address.x_axis, &caps.address.y_axis);
let extent = super::window::reachable_blocks(caps, y.boundary);
reachable(caps, extent)?;
let count = frames_on_medium(caps);
debug!(
count,
pitch = y.boundary,
extent,
"framed from the address page"
);
let frames = (0..count)
.map(|n| {
let top = y.address_range.start + n * y.boundary;
Rect {
top,
left: x.address_range.start,
bottom: top + extent,
right: x.address_range.start + x.boundary,
}
})
.collect();
Ok(Boundary { frames })
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scan::window::tests::caps;
#[test]
fn a_medium_longer_than_the_gate_is_framed_along_its_range() {
let mut caps = caps();
assert_eq!(frames(&caps).expect("frames").frames.len(), 1);
caps.address.y_axis.address_range = (0..=111324).into();
caps.address.y_axis.boundary = 4453;
caps.address.line_gap = 0;
let found = frames(&caps).expect("frames").frames;
assert_eq!(found.len(), 25);
assert_eq!(found[0].bottom - found[0].top, 4453);
assert_eq!(found[24].top, 24 * 4453);
}
}