use tiny_skia::Pixmap;
pub fn bilinear(src: &Pixmap, dst_size: u32) -> Option<Pixmap> {
let sw = src.width() as f32;
let sh = src.height() as f32;
if dst_size == 0 || src.width() < dst_size || src.height() < dst_size {
return None;
}
let mut dst = Pixmap::new(dst_size, dst_size)?;
let data = src.data();
let dst_data = dst.data_mut();
let src_stride = src.width() as usize * 4;
for y in 0..dst_size {
let sy = (y as f32 + 0.5) * sh / dst_size as f32 - 0.5;
let y0 = sy.floor().max(0.0) as usize;
let y1 = (sy.ceil() as usize).min(src.height() as usize - 1);
let wy = sy - y0 as f32;
for x in 0..dst_size {
let sx = (x as f32 + 0.5) * sw / dst_size as f32 - 0.5;
let x0 = sx.floor().max(0.0) as usize;
let x1 = (sx.ceil() as usize).min(src.width() as usize - 1);
let wx = sx - x0 as f32;
let (w00, w10, w01, w11) = (
(1.0 - wx) * (1.0 - wy),
wx * (1.0 - wy),
(1.0 - wx) * wy,
wx * wy,
);
let (i00, i10, i01, i11) = (
y0 * src_stride + x0 * 4,
y0 * src_stride + x1 * 4,
y1 * src_stride + x0 * 4,
y1 * src_stride + x1 * 4,
);
let o = (y as usize * dst_size as usize + x as usize) * 4;
for c in 0..4 {
let v = data[i00 + c] as f32 * w00
+ data[i10 + c] as f32 * w10
+ data[i01 + c] as f32 * w01
+ data[i11 + c] as f32 * w11;
dst_data[o + c] = (v + 0.5) as u8;
}
}
}
Some(dst)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn solid_color_unchanged() {
let mut src = Pixmap::new(8, 8).unwrap();
src.fill(tiny_skia::Color::from_rgba8(30, 60, 90, 255));
let dst = bilinear(&src, 4).unwrap();
for px in dst.data().chunks(4) {
assert_eq!(&px[..3], &[30, 60, 90], "纯色缩小后应保持一致");
assert_eq!(px[3], 255);
}
}
#[test]
fn half_size_and_blend() {
let mut src = Pixmap::new(16, 16).unwrap();
src.fill(tiny_skia::Color::WHITE);
let d = src.data_mut();
for y in 0..16usize {
for x in 0..8usize {
let o = (y * 16 + x) * 4;
d[o] = 0;
d[o + 1] = 0;
d[o + 2] = 0;
}
}
let dst = bilinear(&src, 8).unwrap();
assert_eq!(dst.width(), 8);
for y in 0..8usize {
let o = (y * 8 + 3) * 4;
assert!(dst.data()[o] < 30, "左半应偏黑: {}", dst.data()[o]);
let o = (y * 8 + 4) * 4;
assert!(dst.data()[o] > 225, "右半应偏白: {}", dst.data()[o]);
}
}
}