use crate::calc::graph::Extent;
use crate::calc::{Computed, Value, Values};
use crate::projection::Window;
use crate::sheet::{A1Range, Content, Workbook};
pub fn bytes(model: &Workbook, values: &dyn Values, window: &Window) -> Vec<u8> {
let r = window.range;
let width = (r.right - r.left + 1) as usize;
let height = (r.bottom - r.top + 1) as usize;
let mut out = vec![0u8; width * height];
let Some(si) = model.find_sheet(&window.sheet) else {
return out;
};
let lags = values.lags(si);
for (&(row, col), cell) in model.sheets[si]
.cells
.range((r.top, 0)..=(r.bottom, u32::MAX))
{
if col < r.left || col > r.right {
continue;
}
let at = (row - r.top) as usize * width + (col - r.left) as usize;
let newer = if lags {
values.content((si, row, col))
} else {
None
};
out[at] = match newer.as_ref().unwrap_or(&cell.content) {
Content::Number(n) => byte(*n),
Content::Text(_) | Content::Bool(_) | Content::Error(_) => 255,
_ => match values.computed((si, row, col)) {
Some(Computed::Value(Value::Number(n))) => byte(*n),
Some(_) => 255,
None => 0,
},
};
}
out
}
pub fn byte(n: f64) -> u8 {
if (0.0..=254.0).contains(&n) && n.fract() == 0.0 {
n as u8
} else {
255
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Raster {
top: u32,
left: u32,
width: usize,
height: usize,
bytes: Vec<u8>,
}
pub(crate) fn fits(extent: Extent, limit: u64) -> bool {
extent.is_some_and(|((top, left), (bottom, right))| {
u64::from(right - left + 1) * u64::from(bottom - top + 1) <= limit
})
}
impl Raster {
pub(crate) fn over(extent: Extent, limit: u64, old: Option<Raster>) -> Option<Raster> {
if !fits(extent, limit) {
return None;
}
let ((top, left), (bottom, right)) = extent?;
let width = (right - left + 1) as usize;
let height = (bottom - top + 1) as usize;
let mut bytes = old.map(|r| r.bytes).unwrap_or_default();
bytes.clear();
bytes.resize(width * height, 0);
Some(Raster {
top,
left,
width,
height,
bytes,
})
}
pub(crate) fn set(&mut self, row: u32, col: u32, byte: u8) -> bool {
let (Some(r), Some(c)) = (row.checked_sub(self.top), col.checked_sub(self.left)) else {
return false;
};
let (r, c) = (r as usize, c as usize);
if r >= self.height || c >= self.width {
return false;
}
self.bytes[r * self.width + c] = byte;
true
}
pub fn window(&self, range: &A1Range) -> Vec<u8> {
let width = (range.right - range.left + 1) as usize;
let height = (range.bottom - range.top + 1) as usize;
let mut out = vec![0u8; width * height];
let bottom = self.top + (self.height as u32 - 1);
let right = self.left + (self.width as u32 - 1);
let (top, left) = (range.top.max(self.top), range.left.max(self.left));
let (last_row, last_col) = (range.bottom.min(bottom), range.right.min(right));
if top > last_row || left > last_col {
return out;
}
let n = (last_col - left + 1) as usize;
for row in top..=last_row {
let from = (row - self.top) as usize * self.width + (left - self.left) as usize;
let to = (row - range.top) as usize * width + (left - range.left) as usize;
out[to..to + n].copy_from_slice(&self.bytes[from..from + n]);
}
out
}
}
pub(crate) fn kept_byte(content: &Content, value: Option<&Computed>) -> u8 {
match content {
Content::Number(n) => byte(*n),
Content::Text(_) | Content::Bool(_) | Content::Error(_) => 255,
Content::Formula(_)
| Content::SharedMaster { .. }
| Content::SharedChild { .. }
| Content::DynamicFormula(_) => formula_byte(value),
}
}
pub(crate) fn formula_byte(value: Option<&Computed>) -> u8 {
match value {
Some(Computed::Value(Value::Number(n))) => byte(*n),
Some(_) => 255,
None => 0,
}
}