Skip to main content

repame_view2d/
cursor.rs

1//! Hardware-cursor frames: one strip in, one magnified RGBA frame out.
2//!
3//! The strip is a horizontal sheet of `frames` cells of `cell` size; the
4//! caller uploads the returned pixels as a cursor image. [`CursorFrame::key`]
5//! is the identity of the decoded frame, so a caller can cache the upload and
6//! only redo it when the key changes.
7
8/// One decoded + magnified hardware-cursor frame.
9#[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}