use std::rc::Rc;
use crabshell_ui::{CaptureFrame, Fit};
use tiny_skia::PixmapRef;
use crate::PixelFormat;
use crate::icons::halve;
use crate::images;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Turn {
pub quarters: u8,
pub flipped: bool,
}
impl Turn {
fn sideways(self) -> bool {
self.quarters % 2 == 1
}
}
pub fn capture_frame(
pixels: &[u8],
width: u32,
height: u32,
format: PixelFormat,
opaque: bool,
turn: Turn,
cover: (u32, u32),
) -> Option<CaptureFrame> {
let source = PixmapRef::from_bytes(pixels, width, height)?;
let cover = if turn.sideways() {
(cover.1, cover.0)
} else {
cover
};
let (kept_width, kept_height) = frame_size((width, height), cover);
let mut first = if kept_width * 2 <= width && kept_height * 2 <= height {
halve(source).ok()?
} else {
source.to_owned()
};
if opaque {
for pixel in first.data_mut().as_chunks_mut::<4>().0 {
pixel[3] = 255;
}
}
let mut kept = if (first.width(), first.height()) == (kept_width, kept_height) {
first
} else {
images::scale(first.as_ref(), kept_width, kept_height, Fit::Fill).ok()?
};
if format == PixelFormat::Argb8888 {
for pixel in kept.data_mut().as_chunks_mut::<4>().0 {
pixel.swap(0, 2);
}
}
let (width, height, rgba) = upright(kept_width, kept_height, kept.take(), turn);
Some(CaptureFrame {
width,
height,
rgba: Rc::from(rgba),
})
}
fn upright(width: u32, height: u32, rgba: Vec<u8>, turn: Turn) -> (u32, u32, Vec<u8>) {
if turn == Turn::default() {
return (width, height, rgba);
}
let (w, h) = (width as usize, height as usize);
let (out_w, out_h) = if turn.sideways() { (h, w) } else { (w, h) };
let mut out = vec![0; rgba.len()];
for y in 0..out_h {
for x in 0..out_w {
let (bx, by) = match turn.quarters % 4 {
0 => (x, y),
1 => (y, h - 1 - x),
2 => (w - 1 - x, h - 1 - y),
_ => (w - 1 - y, x),
};
let bx = if turn.flipped { w - 1 - bx } else { bx };
let (from, to) = ((by * w + bx) * 4, (y * out_w + x) * 4);
out[to..to + 4].copy_from_slice(&rgba[from..from + 4]);
}
}
(out_w as u32, out_h as u32, out)
}
pub fn frame_size(source: (u32, u32), cover: (u32, u32)) -> (u32, u32) {
let across = f64::from(cover.0) / f64::from(source.0.max(1));
let down = f64::from(cover.1) / f64::from(source.1.max(1));
let factor = across.max(down).min(1.0);
let side = |length: u32| ((f64::from(length) * factor).round() as u32).max(1);
(side(source.0), side(source.1))
}