1#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CursorFrame {
11 pub rgba: Vec<u8>,
12 pub size: [u16; 2],
13 pub hotspot: [u16; 2],
14 pub key: (i32, [u32; 3], u64, u32),
15}
16
17pub fn cursor_frame(
18 strip: &[u8],
19 strip_w: u32,
20 strip_h: u32,
21 frames: u32,
22 frame: i32,
23 cell: (u32, u32),
24 origin: (f32, f32),
25 tint: [f32; 4],
26 mag: u32,
27 strip_hash: u64,
28) -> Option<CursorFrame> {
29 let frames = frames.max(1) as i32;
30 let frame = frame.clamp(0, frames - 1);
31 let (cw, ch) = (cell.0.max(1), cell.1.max(1));
32 if strip_w < cw * (frame as u32 + 1) || strip_h < ch {
33 return None;
34 }
35 let mag = mag.clamp(1, 8);
36 let tint_u8 = [
37 (tint[0].clamp(0.0, 1.0) * 255.0) as u32,
38 (tint[1].clamp(0.0, 1.0) * 255.0) as u32,
39 (tint[2].clamp(0.0, 1.0) * 255.0) as u32,
40 ];
41 let key = (frame, tint_u8, strip_hash, mag);
42 let fx = (frame as u32 * cw) as usize;
43 let (dw, dh) = (cw * mag, ch * mag);
44 if dw > 2048 || dh > 2048 {
45 return None;
46 }
47 let mut px = Vec::with_capacity((dw * dh * 4) as usize);
48 for row in 0..ch {
49 let start = ((row * strip_w) as usize + fx) * 4;
50 let end = start + (cw as usize) * 4;
51 let cell = strip.get(start..end)?;
52 let mut mag_row = Vec::with_capacity((cw * mag * 4) as usize);
53 for pix in cell.chunks_exact(4) {
54 let t = [
55 (pix[0] as u32 * tint_u8[0] / 255) as u8,
56 (pix[1] as u32 * tint_u8[1] / 255) as u8,
57 (pix[2] as u32 * tint_u8[2] / 255) as u8,
58 pix[3],
59 ];
60 for _ in 0..mag {
61 mag_row.extend_from_slice(&t);
62 }
63 }
64 for _ in 0..mag {
65 px.extend_from_slice(&mag_row);
66 }
67 }
68 let (w16, h16) = (dw.min(2048) as u16, dh.min(2048) as u16);
69 Some(CursorFrame {
70 rgba: px,
71 size: [w16, h16],
72 hotspot: [
73 (origin.0 * mag as f32)
74 .round()
75 .clamp(0.0, w16.saturating_sub(1) as f32) as u16,
76 (origin.1 * mag as f32)
77 .round()
78 .clamp(0.0, h16.saturating_sub(1) as f32) as u16,
79 ],
80 key,
81 })
82}