use crate::{Result, error::Error};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlideTextureLayout {
source: [u16; 2],
allocation: [u16; 2],
stride: [u16; 2],
}
impl GlideTextureLayout {
pub fn new(source: [u16; 2], text_scale: Option<[i32; 2]>) -> Result<Self> {
let error = |reason: &str| {
Error::Parse(format!(
"Glide texture {}x{}: {reason}",
source[0], source[1]
))
};
if source.iter().any(|&v| v == 0 || v > 256) {
return Err(error("source dimensions must be in 1..=256"));
}
let factors = text_scale.unwrap_or([4, 2]);
for (factor, limit) in factors.into_iter().zip([256, 128]) {
if factor <= 0 || factor > limit || !(factor as u32).is_power_of_two() {
return Err(error("unsupported SYSTEM.INI [3dfx] text_scale factor"));
}
}
let rounded = source.map(u16::next_power_of_two);
let stride = rounded.map(|size| match size {
256 => factors[0] as u16,
128 => factors[1] as u16,
_ => 1,
});
let mut allocation = std::array::from_fn(|i| rounded[i] / stride[i]);
let minimum = allocation[0].max(allocation[1]) / 8;
allocation = allocation.map(|size| size.max(minimum));
Ok(Self {
source,
allocation,
stride,
})
}
#[must_use]
pub fn source_dimensions(self) -> [u16; 2] {
self.source
}
#[must_use]
pub fn allocation_dimensions(self) -> [u16; 2] {
self.allocation
}
#[must_use]
pub fn sampling_stride(self) -> [u16; 2] {
self.stride
}
#[must_use]
pub fn coordinate_scale(self) -> [f32; 2] {
let maximum = f64::from(self.allocation[0].max(self.allocation[1]));
std::array::from_fn(|i| {
(f64::from(self.source[i] / self.stride[i]) * 256.0
/ (maximum * f64::from(self.source[i]))) as f32
})
}
#[must_use]
pub fn sampling_level(self) -> u32 {
8 - self.allocation[0].max(self.allocation[1]).ilog2()
}
pub fn sample_indexed(self, source: &[u8]) -> Result<Vec<u8>> {
let [width, height] = self.source.map(usize::from);
if source.len() != width * height {
return Err(Error::Parse(format!(
"Glide texture {width}x{height}: expected {} indexed pixels, got {}",
width * height,
source.len()
)));
}
let [aw, ah] = self.allocation.map(usize::from);
let [sx, sy] = self.stride.map(usize::from);
let mut pixels = vec![0; aw * ah];
for (y, source_y) in (0..height).step_by(sy).enumerate() {
for (x, source_x) in (0..width).step_by(sx).enumerate() {
pixels[y * aw + x] = source[source_y * width + source_x];
}
}
Ok(pixels)
}
}